1 / 55

Hi Haskell

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

avery
Download Presentation

Hi Haskell

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Hi Haskell

  2. “A language that doesn’t effect how you think about programming is not worth learning” Alan Perlis

  3. 提纲 • 概述 • 若干语言特性(是什么让Haskell如此独特?) • Lambda,Curry • Algebraic Data Type,Type Class • Purity, Lazy Evaluation • 也许来个中场休息?(optional) • 对并行与并发的支持 • 若干例子以及性能问题 • Haskell在工业界的应用 • 总结

  4. 编程范式(Programming Paradigm) 令一门编程语言区别于其它语言的根本在于它所提供的编程范式 编程范式指的是编程活动中所遵循的根本风格。它包括选取何种基本元素来构成程序(比如对象,函数,变量,约束条件等等)以及用何种方式来表达计算过程(比如赋值、表达式求值,数据流等等)。--维基百科

  5. 你知道并用过多少种编程范式? Imperative Declarative Object-Oriented Aspect-Oriented Event-Driven Functional Generic Logic Concurrent

  6. 其实还有更多!

  7. Functional Programming 在FP这种编程范式里,计算是通过对函数的求值(evaluation)来组织的,它尽量避免涉及状态与可变(mutable)数据。FP强调的是函数的施用(application),与之相对的命令式编程强调的则是状态的改变。 几乎所有的主流语言都是偏向命令式的。

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

  9. Lambda表达式(匿名函数) (\x y->x*10+y) 5 9 --59 大部分时候可以象动态语 言那样不写类型声明,编 译器会尽全力推导出表达 式的类型,如果推导失败, 它会抱怨的 //JavaScript (function (x,y){ return x*10 + y })(5,9)

  10. First-Class Function & High Order Function • 函数是语言中的一等公民,意味着它享有和Int、Float这些基本类型一样的公民权利: • 函数本身可以作为参数传递 • 函数本身可以作为返回值 • 函数本身可以作为复合类型数据的一部分 • 高阶函数是这样一个东东: • 或者它的参数是另外的函数 • 或者它的返回值是一个函数

  11. 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

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

  13. Example - FieldTriphttp://www.haskell.org/haskellwiki/FieldTrip talk is cheap show me the running code

  14. 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

  15. Example - 函数式3D建模 采用类似的思想,我们还能描述更复杂的3D场景

  16. Algebraic Data Type 带一个参数的Constructor 递归的类型定义 不带参数的Constructor • data是关键字,用来定义新的数据类型,可以带零或多个类型参数 • 每个新类型有一或多个constructor用来创建该类型的值 • 每个constructor有零或多个参数,参数的类型可以是类型参数指定的类型以及任意可见类型 data Maybe a = Just a | Nothing data Tree a = Empty | Leaf a | Node (Tree a)(Tree a)

  17. Algebraic Data Type • 用具体的参数类型来具化Maybe:Maybe Int • Just 20 • Nothing • 当我们拿到一个Maybe Int的值,如何知道它包裹的整型数到底是多少?

  18. 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

  19. 某种多态的函数 • 试着定义这样一个函数,它判断某个值是否在list里 • 作为懒惰的程序员,我们当然希望函数的定义只写一次 • 问题来了,我们怎么样描述这样一种数据类型,它的值是可以进行相等性测试的?

  20. Type Classes OO的class 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

  21. Instance of Type Classes instance Eq (Maybe a) where Just v1 == Just v2 = v1 == v2 Nothing == Nothing = True _ == _ = False

  22. Lazy Evaluation a = [1..] -- infinite list take 3 a -- [1,2,3]

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

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

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

  26. 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

  27. 能用来做事吗? 如果连状态都不能改变,那它有个鸟用! 不要误会,所谓不能改变状态指的是函数求值的时候不允许改变状态,并不意味着程序运行的时候不能改变状态 这是什么意思?

  28. IO Action --putStrLn :: String -> IO () --main :: IO () main = putStrLn "Hi Haskell!" • IO ()是一种IO动作,它是普通的first-class的数据,如果它被执行就会做某些输入输出操作,IO动作只能被其它的IO动作嵌套,main是最外层的IO动作,也是程序的入口点 • 一个IO动作在创建的时候并不会执行,只有在嵌套它的外层动作被执行时才会跟着执行,所以执行和函数求值是不同的 • 所有的IO动作都是由运行程序开始执行main而引发的

  29. Monad • 所有会引发状态改变的动作(包括IO Action)都是一种Monad。 • 听起来象是Monster! • Haskell最大的错误是把Monad叫作Monad!或许应该喊它 "warm fuzzy thing" (暖毛毛)! --Simon Peyton Jones,Haskell设计者之一

  30. Monad Monad m a a inject (a -> m b) bind class Monad m where bind :: m a -> (a -> m b) -> m b inject :: a -> m a

  31. Example - 函数式音乐编程 • 创建一种DSL,既能表达高层的音乐结构,也能表达底层的声音信号处理。 • 什么特性特别有助于在Haskell里创建EDSL? • 纯洁的函数和高阶函数 • Algebraic Data Type • 强类型检查 • 惰性求值 • 二元函数的中缀写法

  32. Example - 函数式音乐编程, Hommage Haskell Offline Music Manipulation And Generation EDSL http://substitut-fuer-feinmotorik.net/projects/haskellommage

  33. 并发与并行

  34. 线程 • 非操作系统线程,极为轻量 • 你可以成千上万地随便创建 forkIO :: IO () -> IO ThreadId forkIO (writeFile "temp.txt" "haskell thread")

  35. 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

  36. Software Transactional Memory(STM) • 一组动作可以作为一个transaction被执行以保证原子性,一旦线程进入这个原子块,那么其它的线程在该原子块退出之前无法看到其做的修改,同样,这个线程在此期间也无法看到别人做的修改 • 在退出原子块的时候,结果要么是a要么是b • transaction成功执行,其它线程可以立刻看见它所做的状态修改 • transaction执行失败,所有的修改被抛弃

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

  38. 事务执行 transferTest = do alice <- newTVar 12 bob <- newTVar 4 transfer 3 alice bob -- atomically:: STM a -> IO a main = atomically transferTest

  39. 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

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

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

  42. 并行FP并不遥远 Map-(Filt)-Reduce(Haskell里叫Fold),是FP里司空见惯的pattern Mapper、Filter和Reducer在处理某个数据集合时并不依赖于其它的数据集合,天生就适合并行化

  43. 并行FP并不遥远 • 所以,Google将此pattern发扬光大,用于大规模并行化数据处理,打造了经典的Map/Reduce编程框架(尽管采用了C++来实现),和Google File System、BigTable一道成为Google搜索技术的三大基石 • Hadoop, Map/Reduce的开源实现,为无数互联网公司所使用 • 其实我们每天都在享受着FP提供的服务

  44. 用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

  45. 异构并行计算 绝大多数的个人电脑中都有一个强劲的并行处理器:显卡中的GPU! 没有道理限制代码只能在CPU上奔跑 对GPU编程:Shader、CUDA、OpenCL GPU的执行模型天生就适合FP GPipe,一种EDSL,也是一种函数式的GPU编程模型,直接用Haskell写GPU程序,从而利用Haskell的种种迷人特性,运行时动态生成OpenGL的GLSL,提交给显卡执行

  46. Example - 函数式GPU编程 GPipe http://www.haskell.org/haskellwiki/GPipe

  47. Parser Combinator 每个程序员或多或少都会干点parse的工作 所谓parse,其实就是将一段无结构的数据(通常是线性的字符串、二进制流)转换成定义良好的结构化数据 有许多工具(比如Lex、Yacc、Flex、Bison、Antlr)自动生成parse代码 Haskell社区首先探索了一种独特的方式来构建复杂的parser,称为Parser Combinator。这种思想纷纷为许多其它语言所借鉴。

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

  49. 这些EDSL的共同点 把你对特定问题的“描述”送进头等舱,成为first-class的 结构化数据,为其选择合适的combinator,使之不断组合产生 更复杂的方案,直到能完全描述出最终的程序意图。

  50. 性能 • 天下武功,唯快不破 --火云邪神 • 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

More Related