功能规划 - 1940年代:阶段性的简约实现

时间:2017-11-05 23:32:37

标签: haskell lambda functional-programming factorial

我看过约翰休斯的惊人谈话,标题为Why Functional Programming Matters几次,但最近才决定实际尝试实施布尔,整数,当然是阶乘的“极简主义”版本,如{ {3}}。

我实施了truefalseiftezeroonetwoiszero,{{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)))
     | ^^^^^^^^^^^^^^^^^^^^^

注意:完整的错误消息是几页长。

2 个答案:

答案 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))