1

Scala与Haskell的严谨优雅性比较

 1 month ago
source link: https://www.jdon.com/47206.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Scala与Haskell的严谨优雅性比较

函数语言主要优点是秉承数学的严谨性与可推导性,该文比较了纯函数语言Haskell在代数方程上与Scala语言的不同性,突出了Haskell纯函数语言的特点。

Haskell for all: Algebraic side effects

初中或小学数学中我们都学过方程式:
f * (xs + ys) = (f * xs) + (f * ys)
左边的方程等同于右边的方程,而在函数语言中也是秉承这种交换性的,假设我们做个符号替换:
1. 使用 Haskell的map函数替换数学的乘号。
2. 使用Haskell的 ++ 操作符替换数学的加号。

这样上面的方程式就变成了Haskell的等式:
map f (xs ++ ys) = (map f xs) ++ (map f ys)

也就是说,将集合xs和ys串联起来后,然后基于串联后的集合使用名为f的map函数,其结果等同于:对xs和ys每个集合单独使用名为f的map函数,然后串联这两个结果。

使用Haskell REPL运行效果如下:

>>> map (+ 1) ([2, 3] ++ [4, 5])
[3,4,5,6]
>>> (map (+ 1) [2, 3]) ++ (map (+ 1) [4, 5])
[3,4,5,6]

上述代数效果并不是在每个号称函数语言中都会有,其他语言使用这样的方程式会产生副作用,无副作用是函数语言引以为傲的主要特点,因为其他语言不会像 Haskell那样等式两边产生相同的顺序效果,如[3,4,5,6],有的可能是[3,5,4,6],这样就没有代数方程的严谨性了。

让我们使用混合式语言Scala实现看看:

>>> def xs() = { print("!"); Seq(1, 2) }
>>> def ys() = { print("?"); Seq(3, 4) }
>>> def f(x : Int) = { print("*"); x + 1 }

使用串联与map函数先后不同就会导致结果顺序不同:

>>> (xs() ++ ys()).map(f)
!?****res0: Seq[Int] = List(2, 3, 4, 5)
>>> (xs().map(f)) ++ (ys().map(f))
!**?**res1: Seq[Int] = List(2, 3, 4, 5)

第一行中,两个集合首先串联,然后针对串联结果使用map函数,结果是,首先打印出"!"和"?",然后是f函数对每个元素的结果,打印出4次"*";而在第二行,对xs集合每个元素使用f函数后,在同样对ys采取同样操作,打印的结果和前面顺序就不同了,不是"!?",而是"!*","?"符号夹在4个"*"中间了。

这表明,语句的前后顺序在Scala会导致不同的程序结果,这种语句很显然没有代数方程的特点和严谨性,这样的语句是无法可推导的,经不起推敲的,有副作用的,不是可交换性的associative。

那么Scala是不是没有办法解决呢?原文给出了Scala的复杂解决方案:

-- f  *  (xs  +  ys) = (f  *  xs)  +  (f  * ys)
f =<< (xs <|> ys) = (f =<< xs) <|> (f
=<< ys)

测试结果:

>>> import Control.Applicative
>>> import Pipes
>>> let xs = do { lift (putChar '!'); return 1
<|> return 2 }
>>> let ys = do { lift (putChar '?'); return 3
<|> return 4 }
>>> let f x = do { lift (putChar '*'); return (x + 1) }
>>> runListT (f =<< (xs <|> ys)) -- Note:
`runListT` discards the result
!**?**>>> runListT ((f =<< xs) <|> (f =<< ys))
!**?**>>>

现在函数的顺序是幂等的。

对比一看,Haskell的优雅与简单一目了然,很多人怀疑Haskell在消除副作用以后会破坏数学的优雅性,很显然这不是真的。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK