我想出了一个很好的练习,但做不到。
我们的想法是尝试用这样的方式来表达罗马数字,这样类型检查器就可以告诉我数字是否有效。
{-# LANGUAGE RankNTypes
, MultiParamTypeClasses #-}
data One a b c = One a deriving (Show, Eq)
data Two a b c = Two (One a b c) (One a b c) deriving (Show, Eq)
data Three a b c = Three (One a b c) (Two a b c) deriving (Show, Eq)
data Four a b c = Four (One a b c) (Five a b c) deriving (Show, Eq)
data Five a b c = Five b deriving (Show, Eq)
data Six a b c = Six (Five a b c) (One a b c) deriving (Show, Eq)
data Seven a b c = Seven (Five a b c) (Two a b c) deriving (Show, Eq)
data Eight a b c = Eight (Five a b c) (Three a b c) deriving (Show, Eq)
data Nine a b c d e = Nine (One a b c) (One c d e) deriving (Show, Eq)
data Z = Z deriving (Show, Eq) -- dummy for the last level
data I = I deriving (Show, Eq)
data V = V deriving (Show, Eq)
data X = X deriving (Show, Eq)
data L = L deriving (Show, Eq)
data C = C deriving (Show, Eq)
data D = D deriving (Show, Eq)
data M = M deriving (Show, Eq)
i :: One I V X
i = One I
v :: Five I V X
v = Five V
x :: One X L C
x = One X
l :: Five X L C
l = Five L
c :: One C D M
c = One C
d :: Five C D M
d = Five D
m :: One M Z Z
m = One M
infixr 4 #
class RomanJoiner a b c where
(#) :: a -> b -> c
instance RomanJoiner (One a b c) (One a b c) (Two a b c) where
(#) = Two
instance RomanJoiner (One a b c) (Two a b c) (Three a b c) where
(#) = Three
instance RomanJoiner (One a b c) (Five a b c) (Four a b c) where
(#) = Four
instance RomanJoiner (Five a b c) (One a b c) (Six a b c) where
(#) = Six
instance RomanJoiner (Five a b c) (Two a b c) (Seven a b c) where
(#) = Seven
instance RomanJoiner (Five a b c) (Three a b c) (Eight a b c) where
(#) = Eight
instance RomanJoiner (One a b c) (One c d e) (Nine a b c d e) where
(#) = Nine
main = print $ v # i # i这可能有不同的方法,而且解决方案是不完整的,但是现在我需要理解为什么它抱怨没有RomanJoiner (一个I V)(一个I V) b0的实例,而我认为我声明了这样一个合并器。
发布于 2013-08-13 19:43:00
问题是,选择实例并不是基于唯一有效的实例:一个扩展FunctionalDependencies有助于获得更多的类型推断。启用该功能,并使用| a b -> c表示可以从a和b的类型推断出a # b的类型。不幸的是,这并不是您唯一需要做的事情,因为您将得到错误的Functional dependencies conflict between instance declarations。使用HList中定义的某些类(这些类可以在其他任何地方定义),冲突的两个实例可以组合成一个实例,其中两个实例(如果计算错误)将根据某些类型是否相等来选择可能的结果。
关于这个解决方案的一些评论是丑陋的:
hCond),则不必再次在值级别复制类型级别上正在发生的事情(HCond与instance Show I where show _ = "I")。TypeFamilies,可以消除许多中间类型变量ba, bb, bc, babc ...。
{-# LANGUAGE RankNTypes,MultiParamTypeClasses,FunctionalDependencies,ScopedTypeVariables,UndecidableInstances,FlexibleContexts,FlexibleInstances #-} import Data.HList隐藏((#))导入Data.HList.TypeEqGeneric1导入Data.HList.TypeCastGeneric1 Unsafe.Coerce数据a a b c=1 a派生(Two,Eq)数据2 a b=2(A B c) (1 A B c)派生(Show,Eq)数据3 a c=3(A B c) (A B c) (2 A B C)派生(Show)Eq)数据4 a b c=4(1 A B c) (5 A B c)派生数据5 a c=5 b c=5 b b c=6 a b=6(5 A B c) (1 A B c)派生数据7 a b c=7(5 A B c) (2 A B c)派生(Show,Eq)数据8 a b=8(5 A B)(3 A B c)Eq)数据9 a b c d e=9(1 A B)(1 C D e)派生(Show,Eq)数据Z=Z派生(Show,Eq) --对最后一级数据I=i派生(Show,Eq)数据V=V派生(Show,Eq)数据X=X派生(Show,Eq)数据L=L派生(Show,Eq)数据C=C派生(Show,Eq)数据D=D派生(Show,Eq)数据M=M派生(Show,Eq)Eq) I ::1 I V i=1 I v ::5 I V v=5 V x ::1 X L c=1 X 1 1:5 X L C l=5 L c:1 C D M c=1 C d:5 C D M d=5 D m ::1 M Z Z m=1 M infixr 4# class RomanJoiner a c x a b -> c其中(#):a -> b -> c实例RomanJoiner (1 A B c)(2 A B c) (3 A B c)其中(#) =3实例RomanJoiner (1 A B c) (5 A B c) (4 A B c)其中(#) =4实例RomanJoiner (5 A B c) (1 A B c) (6 A B c)其中(#) =6实例RomanJoiner (5 A B c) (2 A B c) (7 A B c)其中(#) =7实例RomanJoiner (5 A B c)(3 A B c) (8 A B c)其中(#) =8数据错误=a bc a‘b’c‘ba的错误实例。(TypeEq a‘ba,TypeEq b’bb‘bb,TypeEq c c’bc,HAnd ba bab,HAnd babc,TypeEq c a‘bn,HCond bn (9 a bc b’c')错误9,HCond babc (2 A B) => RomanJoiner (1 A B)(1 a‘b’c') z,其中(#) x= hCond (未定义: babc) (2 (uc :1 a b c) (uc :1 a b c)) $ hCond (未定义:b‘b’)(hCond (uc :1 a b)c) (uc y ::一个c‘b’c‘)错误,其中uc = unsafeCoerce main = print $v#i#i {-使用ghc 762打印HList-0.2.3 *Main>主七(5 V) (2(1 I) (1 I) -}https://stackoverflow.com/questions/18216545
复制相似问题