2013-06-22 11 views
6

Wenn ich einen Datentyp wie folgt in Haskell haben:Constraint Arten: Pass mehrere Einschränkungen

data A ctx = A (forall a. ctx a => a -> a) 

Dann kann ich Funktionen setzen, die in diesem Datentyp auf Werte von Fahrzeugtypen einer bestimmten Klasse arbeiten:

> A (+3) :: A Num 
A (+3) :: A Num :: A Num 

Aber ist es auch möglich, Funktionen mit mehreren Einschränkungen in diesen Datentyp zu setzen? Ich habe das versucht:

> :t ((+ 3) . succ) 
((+ 3) . succ) :: (Enum c, Num c) => c -> c 
> :t A ((+ 3) . succ) :: A (Enum,Num) 
    Expecting one more argument to `Enum' 
    In an expression type signature: A (Enum, Num) 
    In the expression: A ((+ 3) . succ) :: A (Enum, Num) 

Also das funktioniert nicht. Wie ist es möglich zu tun, was ich will, oder ist es unmöglich? Ich weiß, eine Lösung wäre die Verwendung eines "Dummy" -Datentyps und einer Typfamilie, aber das möchte ich nicht, wenn es wegen seiner Komplexität einen anderen Weg gibt. Außerdem könnte ich in diesem Beispiel einfach (+1) anstelle von succ verwendet haben, aber es gibt komplexere Fälle, in denen eine solche Umwandlung in nur eine Klasse nicht möglich ist.

+0

Was noch typeclass wie 'Klasse (Num a, Enum a) => Foo a' definieren? – thoferon

+0

Ja, das würde natürlich auch funktionieren, aber ich mag es nicht, eine Typklasse zu machen, nur um zwei Kontexte zu kombinieren – bennofs

Antwort

1

Ich habe einen Weg gefunden, die Idee zu verallgemeinern, mehrere Klassen zu einer zu verbinden. Obwohl es ziemlich umständlich zu benutzen ist, funktioniert es.

{-# LANGUAGE ConstraintKinds  #-} 
{-# LANGUAGE EmptyDataDecls  #-} 
{-# LANGUAGE FlexibleContexts  #-} 
{-# LANGUAGE FlexibleInstances  #-} 
{-# LANGUAGE GADTs     #-} 
{-# LANGUAGE KindSignatures  #-} 
{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE OverlappingInstances #-} 
{-# LANGUAGE RankNTypes   #-} 
{-# LANGUAGE ScopedTypeVariables #-} 
{-# LANGUAGE TypeOperators   #-} 
{-# LANGUAGE UndecidableInstances #-} 
module Test where 

import GHC.Exts 

data (:>+<:) ctx1 ctx2 a where 
    C'C :: (ctx1 a, ctx2 a) => (ctx1 :>+<: ctx2) a 

data (:++:) (ctx1 :: * -> Constraint) (ctx2 :: * -> Constraint) = C 

class Ctx2 c1 c2 a where 
    useCtx :: c1 :++: c2 -> a -> ((c1 :>+<: c2) a -> b) -> b 

instance (c1 a, c2 a) => Ctx2 c1 c2 a where 
    useCtx _ a f = f C'C 

fC :: (Ctx2 Num Enum a) => a -> a 
fC a = useCtx (C :: Num :++: Enum) a $ \C'C -> 3 + succ a 

data A ctx = A 
    { call :: forall a. ctx a => a -> a 
    } 

main = print $ call (A fC :: A (Ctx2 Num Enum)) 3 

Es sollte möglich sein Constraint-Aliase zu verwenden Ctx3, Ctx4, ...