(是什么让Haskell如此独特?) Lambda,Curry Algebraic Data Type

Download Report

Transcript (是什么让Haskell如此独特?) Lambda,Curry Algebraic Data Type

Hi Haskell
“A language that doesn’t effect
how you think about
programming is not worth
learning”
Alan Perlis
提纲
• 概述
• 若干语言特性(是什么让Haskell如此独特?)
– Lambda,Curry
– Algebraic Data Type,Type Class
– Purity, Lazy Evaluation
•
•
•
•
•
也许来个中场休息?(optional)
对并行与并发的支持
若干例子以及性能问题
Haskell在工业界的应用
总结
编程范式(Programming Paradigm)
• 令一门编程语言区别于其它语言的根本在于它所提供
的编程范式
• 编程范式指的是编程活动中所遵循的根本风格。它包
括选取何种基本元素来构成程序(比如对象,函数,
变量,约束条件等等)以及用何种方式来表达计算过
程(比如赋值、表达式求值,数据流等等)。--维基
百科
你知道并用过多少种编程范式?
•
•
•
•
•
•
•
•
•
Imperative
Declarative
Object-Oriented
Aspect-Oriented
Event-Driven
Functional
Generic
Logic
Concurrent
其实还有更多!
Functional Programming
• 在FP这种编程范式里,计算是通过对函数的求值
(evaluation)来组织的,它尽量避免涉及状态与可
变(mutable)数据。FP强调的是函数的施用
(application),与之相对的命令式编程强调的则
是状态的改变。
• 几乎所有的主流语言都是偏向命令式的。
简单来说,Haskell 是一门通用的、静态强类型的、
pure(该叫纯粹还是纯洁呢?)的、函数式编程语言。
它提供的最基本的编程范式是FP,另外还能支持其它
多种范式。
Lambda表达式(匿名函数)
(\x y->x*10+y) 5 9
--59
大部分时候可以象动态语
言那样不写类型声明,编
译器会尽全力推导出表达
式的类型,如果推导失败,
它会抱怨的
//JavaScript
(function (x,y){
return x*10 + y
})(5,9)
First-Class Function & High Order Function
• 函数是语言中的一等公民,意味着它享有和Int、
Float这些基本类型一样的公民权利:
– 函数本身可以作为参数传递
– 函数本身可以作为返回值
– 函数本身可以作为复合类型数据的一部分
• 高阶函数是这样一个东东:
– 或者它的参数是另外的函数
– 或者它的返回值是一个函数
Currying(Partial Application)&High Order
--f::Int->Int->Int
--f::Int->(Int>Int)
f x y=x*10 + y
f5 = f 5
f7 = f 7
f5
f5
f7
f7
9
1
9
1
-----
59
51
79
71
//JavaScript
function f(x) {
return function(y) {
return x * 10 + y
}
}
f5 = f(5)
f7 = f(7)
f5(9) // 59
f7(1) // 71
高阶函数的应用 - 函数式3D建模
• 参数化曲面 (R,R) -> (R,R,R)
• 隐式曲面和空间区域:(R,R,R) -> R,通过计算结
果的符号来判断是否在区域内
• 高度图:(R,R) -> R
• 2D曲线: R -> (R,R)
• 变换: (R,R,R) -> (R,R,R)
• 图像: (R,R) -> Color
Example - FieldTrip
http://www.haskell.org/haskellwiki/FieldTrip
talk is cheap
show me the running code
Example - FieldTrip
circle :: Curve2
semiCircle :: Curve2
-- 让曲线绕着Z轴转一圈
revolve :: Curve2 -> ParamSurf
torus :: R -> R -> ParamSurf
-- sr:内径 cr:截面半径
torus sr cr = revolve (const (sr,0) ^+^
const cr *^ circle)
sphere = revolve semiCircle
Example - 函数式3D建模
采用类似的思想,我们还能描述更复杂的3D场景
Algebraic Data Type
 data是关键字,用来定义新的数据类型,可以带零
或多个类型参数
 每个新类型有一或多个constructor用来创建该类
型的值
 每个constructor有零或多个参数,参数的类型可
以是类型参数指定的类型以及任意可见类型
data Maybe a =
data Tree a =
带一个参数的Constructor
Just a
递归的类型
Empty
| Nothing
不带参数的Constructor
定义
| Leaf a
| Node (Tree a)(Tree a)
Algebraic Data Type
• 用具体的参数类型来具化Maybe:Maybe Int
– Just 20
– Nothing
• 当我们拿到一个Maybe Int的值,如何知道它包裹
的整型数到底是多少?
Pattern Matching
• constructor可以作为函数来创建值
• 也可以用在pattern里萃取被包裹的值
tree =
Node (Leaf 4) (Node Empty (Leaf 5))
accum Empty = 0
accum (Leaf a) = a
accum (Node left right) = (accum
left) + (accum right)
accum tree -- 9
某种多态的函数
 试着定义这样一个函数,它判断某个值是否在list
里
 作为懒惰的程序员,我们当然希望函数的定义只写一
次
 问题来了,我们怎么样描述这样一种数据类型,它的
值是可以进行相等性测试的?
Type Classes
class Eq a where
(==) :: a -> a -> Bool
elem :: Eq a => a->[a]->Bool
elem k (x:xs)
| k == x = True
| otherwise = elem k xs
elem k [] = False
OO的class
Instance of Type Classes
instance Eq (Maybe a) where
Just v1 == Just v2 = v1 == v2
Nothing == Nothing = True
_ == _ = False
Lazy Evaluation
a = [1..] -- infinite list
take 3 a -- [1,2,3]
Purity
好吧,Functional我早就明白了,
可“纯粹”是什么?
Purity
variable一旦和某个数据绑定了就不可再修改了,
所以它不再是可变的量,而仅仅是一个不可变的值的名
字。
a = 3
a = 19
Purity
函数不会有side effect,不会对数据做破坏性的修改,不会
改变外部世界的状态,它的输出只依赖它的输入,任何时刻,
给定同样的输入,一定得到同样的输出。数学中的函数从来就具
有这样的性质,可在大多数编程语言中这种美好的性质却被毁了。
Referential Transparent
能用2 * f(x) 来替换f(x) + f(x)吗?
int y = 10;
int f(int i)
{
return i + y++;
}
f(5) + f(5); // 15 + 16 = 31
2 * f(5); // 2 * 15 = 30
能用来做事吗?
• 如果连状态都不能改变,那它有个鸟用!
• 不要误会,所谓不能改变状态指的是函数求值的时候
不允许改变状态,并不意味着程序运行的时候不能改
变状态
• 这是什么意思?
IO Action
--putStrLn :: String -> IO ()
--main :: IO ()
main = putStrLn "Hi Haskell!"
 IO ()是一种IO动作,它是普通的first-class的数据,如
果它被执行就会做某些输入输出操作,IO动作只能被其它的
IO动作嵌套,main是最外层的IO动作,也是程序的入口点
 一个IO动作在创建的时候并不会执行,只有在嵌套它的外层
动作被执行时才会跟着执行,所以执行和函数求值是不同的
 所有的IO动作都是由运行程序开始执行main而引发的
Monad
• 所有会引发状态改变的动作(包括IO Action)都
是一种Monad。
• 听起来象是Monster!
• Haskell最大的错误是把Monad叫作Monad!或许应
该喊它 "warm fuzzy thing" (暖毛毛)!
-- Simon Peyton Jones,Haskell设计者之一
Monad
class Monad m where
bind :: m a -> (a -> m b) -> m b
inject :: a -> m a
Monad
a
m a
inject
(a -> m b)
bind
Example - 函数式音乐编程
• 创建一种DSL,既能表达高层的音乐结构,也能表达
底层的声音信号处理。
• 什么特性特别有助于在Haskell里创建EDSL?
–
–
–
–
–
纯洁的函数和高阶函数
Algebraic Data Type
强类型检查
惰性求值
二元函数的中缀写法
Example - 函数式音乐编程,
Hommage
Haskell Offline Music Manipulation And
Generation EDSL
http://substitut-fuerfeinmotorik.net/projects/haskellommage
并发与并行
线程
• 非操作系统线程,极为轻量
• 你可以成千上万地随便创建
forkIO :: IO () -> IO ThreadId
forkIO (writeFile "temp.txt" "haskell
thread")
Shared Mutable State Concurrency
 Race conditions due to forgotten locks
 Deadlocks resulting from inconsistent
lock ordering
 Corruption caused by uncaught
exceptions
 Lost wakeups induced by omitted
notifications
Software Transactional Memory(STM)
 一组动作可以作为一个transaction被执行以保证原
子性,一旦线程进入这个原子块,那么其它的线程在
该原子块退出之前无法看到其做的修改,同样,这个
线程在此期间也无法看到别人做的修改
 在退出原子块的时候,结果要么是a要么是b
a. transaction成功执行,其它线程可以立刻看见它所做的
状态修改
b. transaction执行失败,所有的修改被抛弃
STM Sample
import Control.Concurrent.STM
type Bal = TVar Float
transfer::Float->Bal->Bal->STM ()
transfer qty fromBal toBal = do
fromQty <- readTVar fromBal
toQty <- readTVar toBal
writeTVar fromBal (fromQty - qty)
writeTVar toBal (toQty + qty)
事务执行
transferTest = do
alice <- newTVar 12
bob <- newTVar 4
transfer 3 alice bob
-- atomically:: STM a -> IO a
main = atomically transferTest
Composable STM
orElse :: STM a -> STM a -> STM a
retry :: STM a
throwSTM :: exception -> STM a
catchSTM :: STM a -> (exception->STM a)
-> STM a
instance Monad STM
complexAction = action1 `orElse` action2
main = atomically complexAction
让我们并行地工作吧!
-- 一个顺序程序
sort :: (Ord a) => [a] -> [a]
sort (x:xs) = lesser ++ x:greater
where lesser = sort [y | y <- xs, y < x]
greater = sort [y | y <- xs, y >= x]
sort _ = []
让我们并行地工作吧!
-- 一个并行程序
import Control.Parallel (par, pseq)
parSort :: (Ord a) => [a] -> [a]
parSort (x:xs) = force greater `par`
(force lesser `pseq` (lesser ++ x:greater))
where lesser = parSort [y | y <- xs, y < x]
greater = parSort [y | y <- xs, y >= x]
parSort _ = []
并行FP并不遥远
• Map-(Filt)-Reduce(Haskell里叫Fold),是FP里司空
见惯的pattern
• Mapper、Filter和Reducer在处理某个数据集合时并不依
赖于其它的数据集合,天生就适合并行化
并行FP并不遥远
• 所以,Google将此pattern发扬光大,用于大规模并行化数
据处理,打造了经典的Map/Reduce编程框架(尽管采用了
C++来实现),和Google File System、BigTable一道
成为Google搜索技术的三大基石
• Hadoop, Map/Reduce的开源实现,为无数互联网公司所使
用
• 其实我们每天都在享受着FP提供的服务
用Haskell做一个简单的MapReduce框架
import Control.Parallel.Strategies
mapReduce ::
Strategy b -- evaluation strategy for mapping
-> (a -> b) -- map function
-> Strategy c -- evaluation strategy for reduction
-> ([b] -> c) -- reduce function
-> [a] -- list to map over
-> c
mapReduce mapStrat mapFunc reduceStrat reduceFunc input
= mapResult `pseq` reduceResult
where mapResult = parMap mapStrat mapFunc input
reduceResult = reduceFunc mapResult `using`
reduceStrat
异构并行计算
• 绝大多数的个人电脑中都有一个强劲的并行处理器:显卡中的
GPU!
• 没有道理限制代码只能在CPU上奔跑
• 对GPU编程:Shader、CUDA、OpenCL
• GPU的执行模型天生就适合FP
• GPipe,一种EDSL,也是一种函数式的GPU编程模型,直接用
Haskell写GPU程序,从而利用Haskell的种种迷人特性,
运行时动态生成OpenGL的GLSL,提交给显卡执行
Example - 函数式GPU编程
GPipe
http://www.haskell.org/haskellwiki/GPipe
Parser Combinator
• 每个程序员或多或少都会干点parse的工作
• 所谓parse,其实就是将一段无结构的数据(通常是
线性的字符串、二进制流)转换成定义良好的结构化
数据
• 有许多工具(比如Lex、Yacc、Flex、Bison、
Antlr)自动生成parse代码
• Haskell社区首先探索了一种独特的方式来构建复杂
的parser,称为Parser Combinator。这种思想
纷纷为许多其它语言所借鉴。
Example - Parser Combinator for JavaScript
• parser是这样一个函数:String -> Result
• Result是一个对象,包含3个字段:
– remaining: 剩下的有待parse的字符串
– matched: 被该parser成功parse的字符串
– ast: parse后生成的抽象语法树
• 有若干预定义的基本的parser,比如
– range("0", "9")
– token("begin")
• parser可以用combinator组合起来,得到一个新的(更复
杂的)parser
– repeat1(alternate(sequence(parser1,
parser2), parser3))
这些EDSL的共同点
把你对特定问题的“描述”送进头等舱,成为first-class的
结构化数据,为其选择合适的combinator,使之不断组合产生
更复杂的方案,直到能完全描述出最终的程序意图。
性能
 天下武功,唯快不破
--火云邪神
 The Computer Language Benchmarks Game
http://shootout.alioth.debian.org/
平均来说,Haskell的速度是C的1/5
 眼见为实——2D水波特效
http://blog.csdn.net/soloist/archive/2
009/04/09/4060081.aspx
真有人用它来做产品吗?
都有谁呢?
本次分享的起因
 前台技术中心要成立兴趣小组学习一门新语言,在比
较过多门语言后,选择了Haskell
 FP正在深刻地影响工业界。比如Scala,它的目的是
将Haskell的思考方式带给Java程序员,已经成为
JVM上的主流语言,正在成为其上的统治性语言
 无论你常用的是什么语言,掌握一门FP尤其是
Haskell这样的纯粹的FP,都会使你成为更好更好
的programmer
关于我
邓际锋
[email protected]
http://blog.csdn.net/soloist
个人研究兴趣包括
 编程语言的设计与实现
 交互式艺术及FP在该领域的应用
 如何为儿童和非技术人员开发富有乐趣的创作工具
谢谢!