Chi*_*dio 11 haskell class parametrize
我有一堂课如下:
class Token a where
symbol :: a -> String
Run Code Online (Sandbox Code Playgroud)
我还希望 的所有实例都有一个返回参数化类型的Token
函数。convert
仅转换就可以正常工作:
class Token a b where
convert :: a -> b
data Egal = One | Two
instance Token Egal Int where
convert One = 111
convert Two = 222
main = print $ show (convert One :: Int)
Run Code Online (Sandbox Code Playgroud)
但是当我尝试同时使用两者时symbol
,convert
我收到了有关歧义的错误。这是我的代码:
class Token a b where
convert :: a -> b
symbol :: a -> String
data Egal = One | Two
instance Token Egal Int where
convert One = 111
convert Two = 222
symbol One = "one"
symbol Two = "two"
main = print $ show (convert One :: Int)
Run Code Online (Sandbox Code Playgroud)
我究竟做错了什么?
编辑:读我自己的问题时,我开始想知道:这是否应该是两个不同的类,并且我的data Egal
节目将两者实例化?
Dan*_*ner 17
正如您在此处定义的那样,您可以拥有具有相同a
但冲突的实例b
。像这样:
instance Token Char Char where
convert = id
symbol c = [c]
instance Token Char Bool where
convert = (>'m')
symbol c = [c, c, c, c]
Run Code Online (Sandbox Code Playgroud)
现在,应该symbol 'x'
是"x"
还是"xxxx"
?两者都是可能的,具体取决于选择上述实例中的哪一个;哪个实例应该用于,因此您应该得到哪个答案是不明确的。symbol
有多种方法可以解决这个问题。一种是简单地允许歧义,并让您自己能够指定在调用站点使用哪个实例。您可以打开AllowAmbiguousTypes
和TypeApplications
扩展;然后:
> symbol @_ @Char 'x' -- or, more explicitly, symbol @Char @Char 'x'
"x"
> symbol @_ @Bool 'x' -- explicitly, symbol @Char @Bool 'x'
"xxxx"
Run Code Online (Sandbox Code Playgroud)
但在许多情况下,您确实希望编译器检查您是否没有创建具有冲突的多个实例a
。然后您可以使用以下任一FunctionalDependencies
扩展:
class Token a b | a -> b where
convert :: a -> b
symbol :: a -> String
instance Token Char Char where {- ... -}
Run Code Online (Sandbox Code Playgroud)
或TypeFamilies
扩展名:
class Token a where
type Conversion a
convert :: a -> Conversion a
symbol :: a -> String
instance Token Char where
type Conversion Char = Char
{- ... -}
Run Code Online (Sandbox Code Playgroud)
在大多数情况下,它们或多或少具有相同的效果:冲突的实例被标记,并且不存在任何歧义。
chi*_*chi 14
在评论中,您写道类型a
只能转换为单一类型b
。您当前的设计允许有多个b
相同的 s a
。
instance Token A B1 where
convert = ...
symbol = ...
instance Token A B2 where
convert = ...
symbol = ...
Run Code Online (Sandbox Code Playgroud)
GHC 为何抱怨?上面,两个定义都有symbol
type A -> String
,因此 GHC 无法仅从 types 中选择要使用的实例。
一种(非理想的解决方案)是启用“模糊类型”Haskell 扩展并在每次symbol
调用时手动选择实例。
main = putStrLn $ symbol @A @B1 someAvalue
Run Code Online (Sandbox Code Playgroud)
最好告诉 Haskell 类型b
是仅根据类型确定的a
,这样就不会产生歧义。这可以使用函数依赖扩展来完成,但在我看来,最好使用类型族扩展来实现。
class Token a where -- no b here
type Converted a -- the type after conversion
convert :: a -> Converted a -- we return that type
symbol :: a -> String
data Egal = One | Two
instance Token Egal where
type Converted Egal = Int -- here we chose Int
convert One = 111
convert Two = 222
symbol One = "one"
symbol Two = "two"
main = print $ convert One -- no annotation needed
Run Code Online (Sandbox Code Playgroud)