我想出了一个很好的练习,但是无法使它发挥作用。
我的想法是尝试表达罗马数字,使得类型检查器会告诉我数字是否有效。
{-# 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(One IVX)(One IVX)b0的实例,而我认为我声明了这样的木匠。
答案 0 :(得分:4)
问题是没有根据实例选择实例
只有一个有效:一个扩展FunctionalDependencies
有助于获得更多
类型推断。启用它,并用| a b -> c
说明类型
可以从a # b
和a
的类型推断出b
。不幸的是,这不是您必须做的唯一事情,因为您会收到错误Functional dependencies conflict between instance declarations
。使用HList中定义的一些类(这些可以在其他任何地方定义),冲突的两个实例可以合并为一个,其中两个(如果计算错误则为3)可能的结果是根据某些类型是否相等来选择的
关于这个解决方案的一些评论是丑陋的:
hCond
vs HCond
)如果你有更懒的Show实例(比如
instance Show I where show
_ = "I"
)。 更多现代扩展TypeFamilies
许多中间类型
变量ba, bb, bc, babc ...
可以消除。
{-# LANGUAGE RankNTypes, MultiParamTypeClasses, FunctionalDependencies, ScopedTypeVariables, UndecidableInstances, FlexibleContexts, FlexibleInstances #-}
import Data.HList hiding ((#))
import Data.HList.TypeEqGeneric1
import Data.HList.TypeCastGeneric1
import Unsafe.Coerce
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 | a b -> c where
(#) :: a -> b -> c
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
data Error = Error
instance forall a b c a' b' c' ba bb bc bab babc z bn nine.
(TypeEq a a' ba,
TypeEq b b' bb,
TypeEq c c' bc,
HAnd ba bb bab,
HAnd bab bc babc,
TypeEq c a' bn,
HCond bn (Nine a b c b' c') Error nine,
HCond babc (Two a b c) nine z) =>
RomanJoiner (One a b c) (One a' b' c') z where
(#) x y = hCond (undefined :: babc)
(Two (uc x :: One a b c) (uc y :: One a b c)) $
hCond (undefined :: bn)
(Nine (uc x :: One a b c) (uc y :: One c b' c'))
Error
where uc = unsafeCoerce
main = print $ v # i # i
{-
Prints with ghc 762, HList-0.2.3
*Main> main
Seven (Five V) (Two (One I) (One I)
-}