2013-06-22 2 views
6

Когда у меня есть тип данных, как следующее в Haskell:Constraint видов: Проходят несколько ограничений

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

Тогда я могу положить функции, которые работают по значениям типов данного класса в этот тип данных:

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

Но можно ли также поместить функции, которые имеют несколько ограничений в этом типе данных? Я пробовал:

> :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) 

Так что это не работает. Как можно делать то, что я хочу, или это невозможно? Я знаю, что одним из решений было бы использовать «фиктивный» тип данных и семейство типов, но я не хочу этого делать, если есть другой способ из-за его сложности. Кроме того, в этом примере я мог бы просто использовать (+1) вместо succ, но есть более сложные случаи, когда такое преобразование только в один класс невозможно.

+0

насчет другого класса типов, как 'класса (Num а, Enum а) => Foo Ā'? – thoferon

+0

Да, это, конечно же, будет работать, но мне не очень нравится создавать класс типа только для объединения двух контекстов – bennofs

ответ

1

Я нашел один из способов обобщить идею объединения нескольких классов в один. Хотя это довольно неудобно использовать, это работает.

{-# 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 

Должна быть возможность использовать псевдонимы ограничений для определения Ctx3, Ctx4 ...