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 为何抱怨?上面,两个定义都有symboltype 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)