hi haskell

55
Hi Haskell

Upload: avery

Post on 13-Jan-2016

71 views

Category:

Documents


0 download

DESCRIPTION

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 在工业界的应用 总结. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Hi Haskell

Hi Haskell

Page 2: Hi Haskell

“A language that doesn’t effect how you think about

programming is not worth learning”

Alan Perlis

Page 3: Hi Haskell

提纲

• 概述• 若干语言特性(是什么让 Haskell 如此独特?)

– Lambda, Curry– Algebraic Data Type, Type Class– Purity, Lazy Evaluation

• 也许来个中场休息? (optional)• 对并行与并发的支持• 若干例子以及性能问题• Haskell 在工业界的应用• 总结

Page 4: Hi Haskell

编程范式 (Programming Paradigm)

• 令一门编程语言区别于其它语言的根本在于它所提供的编程范式

• 编程范式指的是编程活动中所遵循的根本风格。它包括选取何种基本元素来构成程序(比如对象,函数,变量,约束条件等等)以及用何种方式来表达计算过程(比如赋值、表达式求值,数据流等等)。 -- 维基百科

Page 5: Hi Haskell

你知道并用过多少种编程范式?

• Imperative• Declarative• Object-Oriented• Aspect-Oriented• Event-Driven• Functional• Generic• Logic• Concurrent

Page 6: Hi Haskell

其实还有更多!

Page 7: Hi Haskell

Functional Programming

• 在 FP 这种编程范式里,计算是通过对函数的求值(evaluation) 来组织的,它尽量避免涉及状态与可变 (mutable) 数据。 FP 强调的是函数的施用(application) ,与之相对的命令式编程强调的则是状态的改变。

• 几乎所有的主流语言都是偏向命令式的。

Page 8: Hi Haskell

简单来说 ,Haskell 是一门通用的、静态强类型的、pure (该叫纯粹还是纯洁呢? ) 的、函数式编程语言。它提供的最基本的编程范式是 FP ,另外还能支持其它多

种范式。

Page 9: Hi Haskell

Lambda 表达式(匿名函数)

(\x y->x*10+y) 5 9

--59

大部分时候可以象动态语言那样不写类型声明,编译器会尽全力推导出表达式的类型,如果推导失败,它会抱怨的

//JavaScript

(function (x,y){

return x*10 + y

})(5,9)

Page 10: Hi Haskell

First-Class Function & High Order Function

• 函数是语言中的一等公民,意味着它享有和Int、 Float 这些基本类型一样的公民权利:– 函数本身可以作为参数传递– 函数本身可以作为返回值– 函数本身可以作为复合类型数据的一部分

• 高阶函数是这样一个东东:– 或者它的参数是另外的函数– 或者它的返回值是一个函数

Page 11: Hi Haskell

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 9 -- 59

f5 1 -- 51

f7 9 -- 79

f7 1 -- 71

//JavaScript

function f(x) {

return function(y) {

return x * 10 + y

}

}

f5 = f(5)

f7 = f(7)

f5(9) // 59

f7(1) // 71

Page 12: Hi Haskell

高阶函数的应用 - 函数式 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

Page 13: Hi Haskell

Example - FieldTriphttp://www.haskell.org/haskellwiki/FieldTrip

talk is cheap

show me the running code

Page 14: Hi Haskell

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

Page 15: Hi Haskell

Example - 函数式 3D 建模

采用类似的思想,我们还能描述更复杂的 3D 场景

Page 16: Hi Haskell

Algebraic Data Type

data 是关键字,用来定义新的数据类型,可以带零或多个类型参数

每个新类型有一或多个 constructor 用来创建该类型的值

每个 constructor 有零或多个参数,参数的类型可以是类型参数指定的类型以及任意可见类型

data Maybe a =

Just a

| Nothing

data Tree a =

Empty

| Leaf a

| Node (Tree a)(Tree a)

递归的类型定义

带一个参数的 Constructor

不带参数的 Constructor

Page 17: Hi Haskell

Algebraic Data Type

• 用具体的参数类型来具化 Maybe: Maybe Int– Just 20– Nothing

• 当我们拿到一个 Maybe Int 的值,如何知道它包裹的整型数到底是多少?

Page 18: Hi Haskell

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

Page 19: Hi Haskell

某种多态的函数

试着定义这样一个函数,它判断某个值是否在 list里

作为懒惰的程序员,我们当然希望函数的定义只写一次

问题来了,我们怎么样描述这样一种数据类型,它的值是可以进行相等性测试的?

Page 20: Hi Haskell

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

Page 21: Hi Haskell

Instance of Type Classes

instance Eq (Maybe a) where

Just v1 == Just v2 = v1 == v2

Nothing == Nothing = True

_ == _ = False

Page 22: Hi Haskell

Lazy Evaluation

a = [1..] -- infinite list

take 3 a -- [1,2,3]

Page 23: Hi Haskell

Purity

好吧, Functional我早就明白了,可“纯粹”是什么?

Page 24: Hi Haskell

Purity

variable 一旦和某个数据绑定了就不可再修改了,所以它不再是可变的量,而仅仅是一个不可变的值的名字。

a = 3

a = 19

Page 25: Hi Haskell

Purity

函数不会有 side effect ,不会对数据做破坏性的修改,不会改变外部世界的状态,它的输出只依赖它的输入,任何时刻,给定同样的输入,一定得到同样的输出。数学中的函数从来就具有这样的性质,可在大多数编程语言中这种美好的性质却被毁了。

Page 26: Hi Haskell

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

Page 27: Hi Haskell

能用来做事吗?• 如果连状态都不能改变,那它有个鸟用!• 不要误会,所谓不能改变状态指的是函数求值的时候

不允许改变状态,并不意味着程序运行的时候不能改变状态

• 这是什么意思?

Page 28: Hi Haskell

IO Action

IO () 是一种 IO 动作,它是普通的 first-class 的数据,如果它被执行就会做某些输入输出操作, IO 动作只能被其它的 IO 动作嵌套, main 是最外层的 IO 动作,也是程序的入口点

一个 IO 动作在创建的时候并不会执行,只有在嵌套它的外层动作被执行时才会跟着执行,所以执行和函数求值是不同的

所有的 IO 动作都是由运行程序开始执行 main而引发的

--putStrLn :: String -> IO ()

--main :: IO ()

main = putStrLn "Hi Haskell!"

Page 29: Hi Haskell

Monad

• 所有会引发状态改变的动作(包括 IO Action )都是一种 Monad 。

• 听起来象是 Monster ! • Haskell 最大的错误是把 Monad 叫作 Monad !或

许应该喊它 "warm fuzzy thing" (暖毛毛 )! -- Simon Peyton Jones, Haskell 设计者之一

Page 30: Hi Haskell

Monad

class Monad m where

bind :: m a -> (a -> m b) -> m b

inject :: a -> m a

Monadm a

bind

injecta

(a -> m b)

Page 31: Hi Haskell

Example - 函数式音乐编程• 创建一种 DSL ,既能表达高层的音乐结构,也能表

达底层的声音信号处理。• 什么特性特别有助于在 Haskell 里创建 EDSL ?

– 纯洁的函数和高阶函数– Algebraic Data Type– 强类型检查– 惰性求值– 二元函数的中缀写法

Page 32: Hi Haskell

Example - 函数式音乐编程 ,

Hommage

Haskell Offline Music Manipulation And Generation EDSL

http://substitut-fuer-feinmotorik.net/projects/haskellommage

Page 33: Hi Haskell

并发与并行

Page 34: Hi Haskell

线程• 非操作系统线程,极为轻量• 你可以成千上万地随便创建

forkIO :: IO () -> IO ThreadId

forkIO (writeFile "temp.txt" "haskell thread")

Page 35: Hi Haskell

Shared Mutable State Concurrency

Race conditions due to forgotten locksDeadlocks resulting from inconsistent lock ordering

Corruption caused by uncaught exceptions

Lost wakeups induced by omitted notifications

Page 36: Hi Haskell

Software Transactional Memory(STM)

一组动作可以作为一个 transaction 被执行以保证原子性,一旦线程进入这个原子块,那么其它的线程在该原子块退出之前无法看到其做的修改,同样,这个线程在此期间也无法看到别人做的修改

在退出原子块的时候,结果要么是 a 要么是 ba. transaction 成功执行,其它线程可以立刻看见它所做的

状态修改b. transaction 执行失败,所有的修改被抛弃

Page 37: Hi Haskell

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)

Page 38: Hi Haskell

事务执行transferTest = do

alice <- newTVar 12

bob <- newTVar 4

transfer 3 alice bob

-- atomically:: STM a -> IO a

main = atomically transferTest

Page 39: Hi Haskell

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

Page 40: Hi Haskell

让我们并行地工作吧!-- 一个顺序程序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 _ = []

Page 41: Hi Haskell

让我们并行地工作吧!-- 一个并行程序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 _ = []

Page 42: Hi Haskell

并行 FP 并不遥远• Map-(Filt)-Reduce(Haskell 里叫 Fold) ,是 FP 里司

空见惯的 pattern• Mapper、 Filter和 Reducer 在处理某个数据集合时并不依赖于其它的数据集合,天生就适合并行化

Page 43: Hi Haskell

并行 FP 并不遥远• 所以, Google将此 pattern 发扬光大,用于大规模并行化

数据处理,打造了经典的 Map/Reduce 编程框架(尽管采用了 C++ 来实现),和 Google File System、 BigTable一道成为 Google搜索技术的三大基石

• Hadoop, Map/Reduce 的开源实现,为无数互联网公司所使用

• 其实我们每天都在享受着 FP 提供的服务

Page 44: Hi Haskell

用 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

Page 45: Hi Haskell

异构并行计算• 绝大多数的个人电脑中都有一个强劲的并行处理器:显卡中的GPU !

• 没有道理限制代码只能在 CPU上奔跑• 对 GPU 编程: Shader、 CUDA、 OpenCL• GPU 的执行模型天生就适合 FP• GPipe ,一种 EDSL ,也是一种函数式的 GPU 编程模型 ,直接用 Haskell写 GPU 程序,从而利用 Haskell 的种种迷人特性,运行时动态生成 OpenGL的 GLSL ,提交给显卡执行

Page 46: Hi Haskell

Example - 函数式 GPU 编程

GPipe

http://www.haskell.org/haskellwiki/GPipe

Page 47: Hi Haskell

Parser Combinator

• 每个程序员或多或少都会干点 parse 的工作• 所谓 parse ,其实就是将一段无结构的数据(通常

是线性的字符串、二进制流)转换成定义良好的结构化数据

• 有许多工具 ( 比如 Lex、 Yacc、 Flex、 Bison、Antlr) 自动生成 parse 代码

• Haskell 社区首先探索了一种独特的方式来构建复杂的 parser ,称为 Parser Combinator 。这种思想纷纷为许多其它语言所借鉴。

Page 48: Hi Haskell

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

Page 49: Hi Haskell

这些 EDSL 的共同点把你对特定问题的“描述”送进头等舱,成为 first-class 的结构化数据,为其选择合适的 combinator ,使之不断组合产

生更复杂的方案,直到能完全描述出最终的程序意图。

Page 50: Hi Haskell

性能 天下武功,唯快不破 -- 火云邪神

The Computer Language Benchmarks Game http://shootout.alioth.debian.org/平均来说, Haskell 的速度是 C的 1/5

眼见为实—— 2D 水波特效http://blog.csdn.net/soloist/archive/2009/04/09/4060081.aspx

Page 51: Hi Haskell

真有人用它来做产品吗?

Page 52: Hi Haskell

都有谁呢?

Page 53: Hi Haskell

本次分享的起因

前台技术中心要成立兴趣小组学习一门新语言,在比较过多门语言后,选择了 Haskell

FP 正在深刻地影响工业界。比如 Scala ,它的目的是将 Haskell 的思考方式带给 Java 程序员,已经成为JVM 上的主流语言,正在成为其上的统治性语言

无论你常用的是什么语言,掌握一门 FP尤其是Haskell 这样的纯粹的 FP ,都会使你成为更好更好的 programmer

Page 54: Hi Haskell

关于我

邓际锋[email protected]

http://blog.csdn.net/soloist

个人研究兴趣包括编程语言的设计与实现交互式艺术及 FP 在该领域的应用 如何为儿童和非技术人员开发富有乐趣的创作工具

Page 55: Hi Haskell

谢谢!