如何在Haskell上轻松处理类型系统?

sof*_*sof 3 polymorphism haskell type-systems type-inference

Haskell的类型系统功能强大,并且因其数学严谨性和逻辑性而受到欢迎,另一方面,如下所述的天真使我想知道为什么它不能像直觉那样工作?

例如,为什么不能Int转换为Numon x3f1接受Int签名Num

Prelude> let x1 = 1
Prelude> :t x1
x1 :: Num a => a

Prelude> let x2 = 1 :: Int
Prelude> :t x2
x2 :: Int

Prelude> let x3 = (1 :: Int) :: Num a => a
Couldn't match expected type ‘a1’ with actual type ‘Int’

Prelude> let f1 :: Num a => a -> a; f1 = id
Prelude> :t f1 (1 :: Int)
f1 (1 :: Int) :: Int

Prelude> let f2 :: Int -> Int; f2 = id
Prelude> :t f2 1
f2 1 :: Int

Prelude> let f3 :: Num a => a -> Int; f3 = id

Prelude> let f4 :: Num a => Int -> a; f4 = id
Couldn't match type ‘a’ with ‘Int’
Run Code Online (Sandbox Code Playgroud)

我知道一个人应该终于学会了基本的理论如HM型系统到舒适与类型系统处理,甚至还发现了一些不错的作品,例如1,2,34的神秘化了.如果你遇到过这个挑战,你还想推荐什么呢?

@编辑

Prelude> let f5 x5 = x5::Int
Prelude> :t f5
f5 :: Int -> Int

Prelude> let f6 x6 = x6::Num a => a
Couldn't match expected type ‘a1’ with actual type ‘t’
Run Code Online (Sandbox Code Playgroud)

首先,当用类型注释时,它本身x6必须是超类型.然而,串连式注解后的不团结,如果我们再垂头丧气的到.因此,第一推断类型的不满意这里.NumNumx6NumNumInt(x6::Int)::Num a => ax6NumIntNumx6

dup*_*ode 17

为什么不能Int转换为Numx3

  1. Int无法转换为Num因为Int是类型而且Num类型类.这两种实体之间的差异有望在下文中变得清晰.

  2. Int无法转换为其他任何内容,因为Haskell在您使用的意义上没有转换.没有隐式演员表.发生了什么是多态类型专门针对某些确定类型; 然而,一个明确的类型永远不会自动成为别的东西

考虑到这一点,让我们考虑一下你的例子.

Prelude> let x1 = 1
Prelude> :t x1
x1 :: Num a => a
Run Code Online (Sandbox Code Playgroud)

x1这是多态的,这意味着它可以根据您的使用方式采用不同的类型.这种不确定性可以通过类型变量的存在来识别a(类型变量,与具体类型不同,不是大写的).x1虽然是多态的,但在某种程度上受约束的限制 Num a.Num a => a可以读作"具有类型类 实例的任何类型Num",而普通则a表示"任何类型的任何类型".

Prelude> let x2 = 1 :: Int
Prelude> :t x2
x2 :: Int
Run Code Online (Sandbox Code Playgroud)

介绍类型的注释:: Int是指请求Int统一使用的类型1,Num a => a.在这种情况下,仅仅意味着更换型变量aInt.鉴于Int确实有一个实例Num,这是一个有效的移动,并且类型检查器乐意接受它.类型标注专业的多态型的1Int.

Prelude> let x3 = (1 :: Int) :: Num a => a
Couldn't match expected type ‘a1’ with actual type ‘Int’
Run Code Online (Sandbox Code Playgroud)

类型1 :: IntInt.第二个注释要求将其统一起来Num a => a.然而,这是不可能的.一旦类型被专门化,您就不能"忘记"类型并仅通过提供类型注释来恢复特化.也许你正在考虑OOP向上倾斜; 这根本不是一回事.顺便说一句,如果类型检查器被接受x3,你就可以编写x4 = ((1 :: Int) :: Num a => a) :: Double,从而将Inta 转换为a Double.但是,在一般情况下,这种转换无法像这样发生,因为您没有告诉转换是如何完成的; 至于特殊情况,没有任何.(将Inta 转换为a Double肯定是可能的,但它需要适当的函数.例如,您可能会发现它与考虑它的作用类型fromIntegral有关.)

Prelude> let f1 :: Num a => a -> a; f1 = id
Prelude> :t f1 (1 :: Int)
f1 (1 :: Int) :: Int
Run Code Online (Sandbox Code Playgroud)

这里的原则保持不变.唯一的区别是你必须考虑参数类型和结果如何相互关联.类型ida -> a.它特别适合Num a => a -> a.传递一个Int参数进一步专门化它Int -> Int,因此你得到一个类型的结果Int.

Prelude> let f2 :: Int -> Int; f2 = id
Prelude> :t f2 1
f2 1 :: Int
Run Code Online (Sandbox Code Playgroud)

f1有一个多态的类型,你通过提供一个Int参数专门,而f2具有单形类型,所以没有必要专门化它.id是专门从a -> a直接到Int -> Int,而1专门从而Num a => a,Int因为你把它喂给一个需要Int参数的函数.

Prelude> let f3 :: Num a => a -> Int; f3 = id
Couldn't match type ‘a’ with ‘Int’
Run Code Online (Sandbox Code Playgroud)

在这里,你要统一a -> a,类型id,与Num a => a -> Int.但是,如果更换a用,例如,DoubleNum a => a -> Int,你Double -> Int,这不可能有统一a -> a的,因为它改变类型,同时a -> a没有.(这是Thomas M. DuBuisson上面评论的观点:你的实现类型与之不相容id,因为id不能改变任何类型.)

Prelude> let f4 :: Num a => Int -> a; f4 = id
Couldn't match type ‘a’ with ‘Int’
Run Code Online (Sandbox Code Playgroud)

最后,这就像是f3,不同之处在于结果类型而不是参数的不匹配.把不同的自旋这一次,你无法实现Num a => Int -> a通过对特定类型的稳定与函数Num实例(无论是Int,Double等),然后在"向上转型"它Num a => a,因为没有这样的东西作为上溯造型.相反,Num a => Int -> a必须为任何a具有实例的选择工作Num.

  • @sof [1/2]典型的多态(代数)数据类型可以以相同的方式定义.在'数据可能a = Nothing | 只需a`,`Just`构造函数可用于包装任意类型的值.`Maybe`本身就是一个*类型构造函数*的例子,因为它从现有的类型中创建了一个不同的类型(例如从`Int`到`Maybe Int`). (4认同)
  • @sof [2/2]但数字不同.诀窍在于,例如,`1`实际上代表`fromInteger(1 :: Integer)`.`Integer`用作整数文字的"默认"类型,`fromInteger`(这是'Num`的方法)用于转换为适当的类型.这是由[Haskell报告的第6.4.1节](https://www.haskell.org/onlinereport/haskell2010/haskellch6.html#x13-1350006.4)指定的.至于什么是"整数","双重"等等,只要符合报告规范,实现就可以免费使用任何方便/高效的东西. (4认同)
  • @sof添加更多的多态文字结构是语言扩展的问题(例如,`OverloadedStrings`是一个常用的GHC扩展,还有`OverloadedLists`),尽管总是可以选择通过Template Haskell模拟语法扩展(即编译时元编程).(无论如何,如果你有一个你定义的`Foo`类型和一个函数`fromFoo :: FooLike a => Foo - > a`,你可以使用`fromFoo`来重载值的创建,只有语法的与"Num"示例相比,文字的便利性将会丢失.) (4认同)
  • 谢谢你彻底的答复.它不仅有助于澄清这个难题,而且还传达了诸如`多态,约束,类型类型,类型变换,单态,实例,类型,专业化,统一化"等关键词,这些关键词有助于我进一步探索更好地了解Haskell上的类型系统. (3认同)