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

Report
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在该领域的应用
 如何为儿童和非技术人员开发富有乐趣的创作工具
谢谢!

similar documents