Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 如何处理更高级别的类型_Haskell - Fatal编程技术网

Haskell 如何处理更高级别的类型

Haskell 如何处理更高级别的类型,haskell,Haskell,玩教堂的数字。我遇到的情况是,我无法指导GHC类型检查器处理高阶类型 首先,我编写了一个没有任何类型签名的版本: module ChurchStripped where zero z _ = z inc n z s = s (n z s) natInteger n = n 0 (1+) add a b = a b inc {- *ChurchStripped> natInteger $ add (inc $ inc zero) (inc $ inc $ inc zero) 5 -}

玩教堂的数字。我遇到的情况是,我无法指导GHC类型检查器处理高阶类型

首先,我编写了一个没有任何类型签名的版本:

module ChurchStripped where

zero z _ = z
inc n z s = s (n z s)
natInteger n = n 0 (1+)

add a b = a b inc

{-
*ChurchStripped> natInteger $ add (inc $ inc zero) (inc $ inc $ inc zero)
5
-}

mult a b = a zero (add b)

{-
*ChurchStripped> natInteger $ mult (inc $ inc zero) (inc $ inc $ inc zero)
6
-}
mult
的推断类型非常糟糕,因此我尝试使用类型定义清理这些类型:

module Church where

type Nat a = a -> (a -> a) -> a

zero :: Nat a
zero z _ = z

inc :: Nat a -> Nat a
inc n z s = s (n z s)

natInteger :: Nat Integer -> Integer
natInteger n = n 0 (1+)

{- `add :: Nat a -> Nat a -> Nat a` doesn't work, and working signature looks already suspicious -}

add :: Nat (Nat a) -> Nat a -> Nat a
add a b = a b inc

{-
*Church> natInteger $ add (inc $ inc zero) (inc $ inc $ inc zero)
5
-}

mult :: Nat (Nat a) -> Nat (Nat a) -> Nat a
mult a b = a zero (add b)

{-
*Church> natInteger $ mult (inc $ inc zero) (inc $ inc $ inc zero)
6
-}
这是可行的,但类型并不像它们可能的那样干净。以下是我尝试过的定义:

{-# LANGUAGE RankNTypes #-}

module SystemF where

type Nat = forall a. a -> (a -> a) -> a

zero :: Nat
zero z _ = z

inc :: Nat -> Nat
inc n z s = s (n z s)

natInteger :: Nat -> Integer
natInteger n = n 0 (1+)

{- This doesn't work anymore

add :: Nat -> Nat -> Nat
add a b = a b inc

   Couldn't match type `forall a1. a1 -> (a1 -> a1) -> a1'
                 with `a -> (a -> a) -> a'
   Expected type: (a -> (a -> a) -> a) -> a -> (a -> a) -> a
     Actual type: Nat -> a -> (a -> a) -> a
   In the second argument of `a', namely `inc'
   In the expression: a b inc
   In an equation for `add': add a b = a b inc

-}
我想应该可以使用
Nat->Nat->Nat->Nat
类型签名编写
add
,但我不知道如何编写


另外,实际上我是从底部开始的,但用这种方式来表示这个问题可能更容易。

我不太理解
RankNTypes
来解释为什么原始示例不起作用,但是如果将Nat打包到数据类型中,那么它就起作用了:

{-# LANGUAGE RankNTypes #-}

module SystemF where

data Nat = Nat (forall a. (a -> (a -> a) -> a))

zero :: Nat
zero = Nat const

inc :: Nat -> Nat
inc (Nat n) = Nat $ \z s -> s $ n z s

natInteger :: Nat -> Integer
natInteger (Nat n) = n 0 (1+)

add :: Nat -> Nat -> Nat
add (Nat a) b = a b inc

现在,
Nat
类型是一种真正的数据类型,这有助于类型检查器,因为它不必一直处理多态类型,只有在您实际“解包”它时才需要

bennofs是对的,您确实想在这里帮助typechecker,特别是在
添加
中,您需要在
中为所有a实例化
a
。a->(a->a)->a
Nat
(即,所有a...
类型的
相同)

一种方法是引入一个包装多态类型的新类型:

newtype Nat' = N Nat
现在,您可以通过
N
Nat
Nat'
之间切换,然后使用
unN

unN :: Nat' -> Nat
unN (N n) = n
(在这一点上值得注意的是,
newtypenat'=nnat
datanat2=forall a.N2(a->(a->a)->a)是不同的beast)
。后者需要
-XExistentialQuantification
,因为它表示对于
a
的某些特定选择,您可以进行
Nat2
。另一方面,前者仍然表示如果您有
a->(a->a)->a
对于任意的
a
,您可以创建一个
Nat'
。对于
Nat'
,您需要
-XRankNTypes
,但不需要存在主义。)

现在我们还可以使
inc'
增加
Nat'

inc' :: Nat' -> Nat'
inc' (N n) = N (inc n)
我们准备补充:

add :: Nat -> Nat -> Nat
add n m = unN (n (N m) inc')
这样做之所以有效,是因为现在不再试图说服GHC使用多态类型
∀ A.a->(a->a)->a本身,
N
充当提示

例如:

> natInteger (add (inc zero) (inc zero))
2

以下是我对教会数字的实现:

type Nat = forall a . (a→a) → (a→a)

zero :: Nat
zero _ = id

one  :: Nat
one    = id

inc :: Nat → Nat
inc a f = f . a f

add :: Nat → Nat → Nat
add a b f = (a f) . (b f)

mul :: Nat → Nat → Nat
mul a b = a . b

nat :: Integer → Nat
nat 0 = zero
nat n = inc $ nat (n-1)

unnat :: Nat → Integer
unnat f = f (+ 1) 0
翻转它们更容易操作(函数首先应用N次,其次是它的参数)。一切都会自然而然地出现


编辑:此解决方案也受到限制,类型与原始问题不同,在某个时候会出现故障。

似乎通过使用
Data.Proxy
我们可以给GHC一个提示:

{-# LANGUAGE RankNTypes #-}

import Data.Proxy

type Nat = forall a. Proxy a -> (a -> a) -> (a -> a)

zero :: Nat
zero _ _ x = x

suc :: Nat -> Nat
suc n proxy s z = n proxy s (s z)

-- add = \m n f x. m f (n f x)
add :: Nat -> Nat -> Nat
add n m proxy s z = n proxy s (m proxy s z)

-- mult = \m n f. m (n f)
mult :: Nat -> Nat -> Nat
mult m n proxy s z = m proxy (n proxy s) z
然后它就成功了

λ > :t let one = suc zero in add one one 
let one = suc zero in add one one :: Proxy a -> (a -> a) -> a -> a
λ > let one = suc zero in add one one Proxy (1+) 0
2

λ > :t let two = suc (suc zero) in mult two two
let two = suc (suc zero) in mult two two
  :: Proxy a -> (a -> a) -> a -> a
λ > let two = suc (suc zero) in mult two two Proxy (1+) 0
4

forall引入了更高级别的类型,这肯定不是您想要的。@Ingo我不明白为什么不换个说法:您在类型中提到的每个
Nat
都有自己的
a
类型变量,不能与您签名中的任何其他类型变量统一,但显然,我们在写
Nat->Nat
时希望
a
s保持不变。@Ingo,不,真的希望它保持通用性。这有点像
idid
case(这不是很有趣,但很有效)。也许您想将类型定义为
(a->a)->(a->a)
。很好。我仍然想知道为什么最初的示例不起作用,是因为
forall
被移动了,如果不是绑定在数据类型或其他内容中的话。似乎这是目前唯一合理的解决方案,正如ExplicitTypeApplication最快将在ghc 7.12.1中出现一样,用自身量化的类型实例化量化类型变量的能力被称为“不确定性”。系统F(和F-omega)都具有不可预测性,Haskell/GHC(默认情况下)没有。GHC实际上对非指示性类型(使用
-ximpeditivetypes
)有一些支持,但实现不是很可预测的,它侧重于使用多态类型实例化数据类型参数(例如
可能(对于所有的a…)
。@kosmikus,你说得对。这与存在主义无关。更新了我的答案以反映这一点。所以没有任何方法在Haskell中实例化通用限定术语,即显式地给出类型参数?它总是由类型推断检查引擎推断出来吗?@OlegGrenrus它没有特定的语法,只有在GHC的核心语言中。但是,这里有人提出:注意,对于单态类型,只需通过类型注释函数即可强制实例化,例如,
id::Int->Int
id
实例化为type
Int
。但是根据您对多态类型
Nat
的定义,
id::Nat->Nat
将失败。只需将
add
扩展即可(即,将
add::Nat->Nat->a->a
实现为一个包含4个参数的函数)。整个
Nat'
的事情似乎是一条不必要的绕道。我想真正的问题是,这是一个指导GHC在特定地方应用HM gen和inst规则的练习。这对于这种特殊情况来说是一个很好的解决方案,但它仍然不是通用的解决方案。例如,如果我尝试以类似的方式制作一个列表:同样的问题会出现(或者我也可以在那里做一些技巧,但不清楚是哪一个)。我会接受@kosmikus的评论,如果这是一个实际的答案。嗯。显然我误解了这个问题。我的Nat示例实际上也是有限的,类型不正确,它迟早会崩溃。