gpt4 book ai didi

haskell - 在类型族中编写多态函数

转载 作者:行者123 更新时间:2023-12-02 07:19:16 25 4
gpt4 key购买 nike

我昨天正在尝试类型族,并遇到了以下代码的障碍:

  {-# LANGUAGE TypeFamilies #-}

class C a where
type A a
myLength :: A a -> Int

instance C String where
type A String = [String]
myLength = length

instance C Int where
type A Int = [Int]
myLength = length

main = let a1 = [1,2,3]
a2 = ["hello","world"]
in print (myLength a1)
>> print (myLength a2)

这里我有一个与 C 类关联的类型和一个计算关联类型长度的函数。但是上面的代码给了我这个错误:

 /tmp/type-families.hs:18:30:
Couldn't match type `A a1' with `[a]'
In the first argument of `myLength', namely `a1'
In the first argument of `print', namely `(myLength a1)'
In the first argument of `(>>)', namely `print (myLength a1)'
/tmp/type-families.hs:19:30:
Couldn't match type `A a2' with `[[Char]]'
In the first argument of `myLength', namely `a2'
In the first argument of `print', namely `(myLength a2)'
In the second argument of `(>>)', namely `print (myLength a2)'
Failed, modules loaded: none.

但是,如果我将“类型”更改为“数据”,代码将编译并运行:

  {-# LANGUAGE TypeFamilies #-}

class C a where
data A a
myLength :: A a -> Int

instance C String where
data A String = S [String]
myLength (S a) = length a

instance C Int where
data A Int = I [Int]
myLength (I a) = length a

main = let a1 = I [1,2,3]
a2 = S ["hello","world"]
in
print (myLength a1) >>
print (myLength a2)

为什么“长度”在第一种情况下不能按预期工作? “type A String ...”和“type A Int ...”行指定类型“A a”是一个列表,因此 myLength 应分别具有以下类型:“myLength::[String] -> Int”或“myLength::[Int] -> Int”。

最佳答案

嗯。让我们暂时忘记类型。

假设您有两个函数:

import qualified Data.IntMap as IM

a :: Int -> Float
a x = fromInteger (x * x) / 2

l :: Int -> String
l x = fromMaybe "" $ IM.lookup x im
where im = IM.fromList -- etc...

假设存在您关心的某个值n::Int。仅给定 a n 的值,如何找到 l n 的值?当然,你不知道。

这有什么关系?嗯,myLength 的类型是 A a -> Int,其中 A a 是应用“类型函数” 的结果A 到某种类型a。但是,myLength 作为类型类的一部分,类参数 a 用于选择要使用的 myLength 实现。因此,给定某个特定类型 B 的值,对其应用 myLength 会得到 B -> Int 类型>,其中 B ~ A a 并且您需要知道 a 才能查找 myLength 的实现。仅给定 A a 的值,如何找到 a 的值?当然,你不知道。

您可以合理地反对,在您的代码中,函数 A 是可逆的,这与我之前示例中的 a 函数不同。这是事实,但编译器无法对此做任何事情,因为涉及类型类的开放世界假设;理论上,您的模块可以由定义自己实例的另一个模块导入,例如:

instance C Bool where
type A Bool = [String]

傻吗?是的。有效代码?也是的。

在许多情况下,在 Haskell 中使用构造函数可以创建简单单射函数:构造函数引入一个新实体,该实体仅由给定的参数唯一定义,从而使恢复变得简单原始值。这正是代码的两个版本之间的区别;数据族通过为每个参数定义一个新的、不同的类型,使类型函数可逆。

关于haskell - 在类型族中编写多态函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6663547/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com