当我在 haskell 中有如下数据类型时:
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,但在更复杂的情况下,不可能只对一个类进行这种转换。
最佳答案
我找到了一种方法来概括将多个类合并为一个类的想法。虽然使用起来很别扭,但它确实有效。
{-# 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...
关于haskell - 约束种类: Pass multiple constraints,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17254057/