2013-01-04 32 views
5

Pracuję nad aplikatorem, który zawiera monoid do "wyświetlenia" wykonania. Czasami jednak nie dbam o tę część, więc wybór monoidu jest nieistotny, ponieważ nigdy nie zostanie zużyty. I zostały uproszczone, co mam na:Używanie typów więzów i rodzin typów z ograniczeniami "ograniczonymi"

{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE ConstraintKinds #-} 
{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE TypeFamilies #-} 

import GHC.Exts 

class Render a b where render :: a -> b 
instance Render a() where render = const() 

class Merge a where 
    type Renderer a b :: Constraint 
    merge :: Renderer a b => a -> b 

data Foo = Foo Bool 

instance Merge Foo where 
    type (Renderer Foo) m = (Render Bool m) 
    merge (Foo b) = render b 

Render jest wykorzystywana do przekształcania różnych a s na pojedynczym b. Merge jest dużym uproszczeniem mojego faktycznego funktora, ale chodzi o to, że zawiera on rodzinę/ograniczenie typu, a moim zamiarem jest określenie dokładnie tego, czego wymaga Render lub Merge.

Teraz mogę chcą "run" na Merge, ale odrzucić pogląd, który jest podobny do czegoś takiego:

runFoo :: Merge a => a -> Int 
runFoo x = case merge x of() -> 5 

ale to się nie powiedzie, ponieważ:

Nie można wydedukować (Renderer a()) wynikające ze stosowania merge

wybrałem () jak mój monoid ponieważ f orall a, mamy instancję Render a(). Więc jeśli istnieje sposób, aby powiedzieć, że Merge a oznacza po prostu ograniczenia zbiórki Render to działałoby to dobrze. Oczywiście, Merge a jest bardziej ogólne niż to - może dodać arbitralne ograniczenia, co wyjaśnia błąd kompilacji.

Czy mimo to można uzyskać to, co chcę, bez zmiany sygnatury runFoo?

+0

Czy 'Renderer' zawsze zawierać dokładnie jeden' ​​Render'? –

+0

@Tinctorius - nie, zazwyczaj zależy od ilości różnych typów w polach 'Foo' – ocharles

+0

Czy istnieje powód, dlaczego' Merge' nie ma również 'b' jako parametru? –

Odpowiedz

6

To może nie być powiększane, jeśli masz dużo tych przypadków, ale to działa:

class Renderer a() => Merge a where 
    ... 
+0

Nie rozważałem tego. Mogłem odczytać to ograniczenie jako "' Merge's powinno przynajmniej obsługiwać rendering z '()' monoid '. Jednakże powoduje to "UndecidableInstances". – ocharles

+0

"UndecidableInstances nie jest niebezpieczną flagą, nigdy nie sprawi, że program sprawdzający poprawi program, który" pójdzie źle ". Jedyną złą konsekwencją użycia flagi jest funkcja sprawdzania typu, która może nam powiedzieć, że nie może zdecydować, czy nasz program jest dobrze napisany, biorąc pod uwagę stos kontekstu - limit głębi. " http://okmij.org/ftp/Haskell/TypeClass.html#undecidable-inst-defense –

+0

Wiem, że już go używam w 3 innych miejscach;) Ale najlepiej lubię szukać rozwiązań, które tego nie wymagają, nawet jeśli z czysto akademickich powodów. Prawdopodobnie przyjmuję tę odpowiedź, pozostawiając nieco otwartą ziemię na dłużej. To naprawdę działa przyjemnie! – ocharles

Powiązane problemy