我看过约翰休斯的惊人谈话,标题为Why Functional Programming Matters几次,但最近才决定实际尝试实施布尔,整数,当然是阶乘的“极简主义”版本,如{ {3}}。
我实施了true
,false
,ifte
,zero
,one
,two
,iszero
,{{1最后decr
video如下:
fact
我测试了每个函数,除了-- boolean
true x y = x
false x y = y
ifte bool t e = bool t e
-- positive integers
three f x = f $ f $ f x
two f x = f $ f x
one f x = f x
zero f x = x
-- add and multiplication
add m n f x = m f $ n f x
mul m n f x = m (n f) x
-- is zero check
iszero n = n (\_ -> false) true
-- decrement
decr n =
n (\m f x -> f (m f zero))
zero
(\x->x)
zero
-- factorial
fact n =
ifte (iszero n)
one
(mul n (fact (decr n)))
和decr
之外,它们都可以完美地编译和工作。
即使John Hughes在here承诺他的fact
的实现有效,但它无法编译并出现以下错误:
错误:变量不在范围内:incr
我不确定decr
应如何实施incr
。
现在,如果我将它们定义为(\m f x -> f (m incr zero))
并将incr = (+1)
的定义更改为以下内容,则decr
编译并正常工作,但decr
仍会导致编译失败。
fact
在decr n =
n (\m f x -> f (m incr x))
zero
(\x->x)
zero'
的定义中使用的lambda (\m f x -> f (m incr zero))
是否存在错误,或者decr
应该以不同的方式定义?
当我将incr
定义为incr
时,incr n = (\f x -> f (n f x))
工作正常,但decr n
无法编译。这是错误消息的可读部分:
factorial.hs:30:1:错误: •发生检查:无法构造无限类型:
...
|事实n =
| ^^^^^^^^ ......
factorial.hs:33:6:错误: •发生检查:无法构造无限类型:
...
fact n
...
| (mul n(事实(decr n)))
| ^^^^^^^^^^^^^^^^^^^^^
注意:完整的错误消息是几页长。
答案 0 :(得分:2)
看起来你真的很亲密
我可以在JavaScript中使用Church's encodings向您展示如何执行此操作,但不能在Haskell中使用,因为我不知道如何在Haskell(U
下面)中进行一些简单的组合器类型检查
因为JavaScript是严格评估的,所以谓词分支必须包含在lambda中
继续并运行代码段 - 我们计算8!
const True = a => b =>
a ()
const False = a => b =>
b ()
const IsZero = n =>
n (x => False) (True)
const Succ = n =>
f => x => f (n (f) (x))
const Pred = n =>
f => x => n (g => h => h (g (f))) (u => x) (u => u)
const Mult = m => n =>
f => m (n (f))
const Add = m => n =>
m (Succ) (n)
const one = f => x =>
f (x)
const two =
Add (one) (one)
const four =
Add (two) (two)
const eight =
Add (four) (four)
const U = f => f (f)
const Fact = U (f => acc => n =>
IsZero (n)
(z => acc) // thunks used for predicate branches
(z => U (f) (Mult (acc) (n)) (Pred (n)))) (one)
const result =
Fact (eight)
// convert church numeral result to a JavaScript number
console.log ('8! =', result (x => x + 1) (0))
// 8! = 40320
如果你有点作弊,你可以使用JavaScript的?:
三元运算符来实现虚假懒惰 - 我只是展示了这一点,所以你可以用更易读的形式看到Fact
;上述实现仅使用 lambdas
const IsZero = n =>
// cheat by returning JavaScript's true/false booleans
n (x => false) (true)
const Succ = n =>
f => x => f (n (f) (x))
const Pred = n =>
f => x => n (g => h => h (g (f))) (u => x) (u => u)
const Mult = m => n =>
f => m (n (f))
const Add = m => n =>
m (Succ) (n)
const one = f => x =>
f (x)
const two =
Add (one) (one)
const four =
Add (two) (two)
const eight =
Add (four) (four)
const U = f => f (f)
const Fact = U (f => acc => n =>
IsZero (n)
? acc // now we're sorta cheating using JavaScript's ternary here
: U (f) (Mult (acc) (n)) (Pred (n))) (one)
const result =
Fact (eight)
console.log ('8! =', result (x => x + 1) (0))
// 8! = 40320
答案 1 :(得分:1)
首先,让我们尝试明确键入所有内容。天真地,所有这些东西都是在教会职能处理的某种类型上进行参数化的:
type Logical a = a -> a -> a
type Nat a = (a->a) -> a->a
-- boolean
true, false :: Logical a
true x y = x
false x y = y
ifte :: Logical a -> a -> a -> a
ifte = id
incr :: Nat a -> Nat a
incr n f = f . n f
-- integer “literals”
zero, one, two, three :: Nat a
three = incr two
two = incr one
one = incr zero
zero _ = id
-- addition and multiplication
add, mul :: Nat a -> Nat a -> Nat a
add m n f = m f . n f
mul m n f = m $ n f
-- zero check
isZero :: Nat a -> Logical a
isZero n = n (const false) true
......好的,我们遇到了第一个问题:
• Couldn't match expected type ‘Logical a’ with actual type ‘a’
‘a’ is a rigid type variable bound by
the type signature for:
isZero :: forall a. Nat a -> Logical a
at /tmp/wtmpf-file7834.hs:25:1-28
• In the expression: n (const false) true
问题是我们尝试使用Nat
- church-numbers作为函数而不是结果a
应该使用的基础Logical
类型,但是那些逻辑本身。即它实际上是
isZero :: Nat (Logical a) -> Logical a
decr
变得更糟 - 这不起作用:
decr :: Nat a -> Nat a
decr n = n (\m f x -> f (m incr x))
zero
id
zero
因为你试图将这个数字用于isZero
中的逻辑目的,这需要注入另一个Nat
层,但也只是用于传递/递增。在传统的Hindley-Milner中,你需要决定其中一个,所以不可能做出类型检查。
在现代Haskell中,可以使其成为类型检查,通过使参数变为多态:
{-# LANGUAGE RankNTypes, UnicodeSyntax #-}
decr :: (∀ α . Nat α) -> Nat a
为避免携带量词,我们可能会制作另一个同义词:
type ANat = ∀ α . Nat α
那么它只是
decr :: ANat -> Nat a
这种技术也适用于阶乘:
fact :: ANat -> Nat a
fact n = ifte (isZero n)
one
(mul n $ fact (decr n))