# Resources https://repl.it/languages/haskell http://learnyouahaskell.com/chapters http://book.realworldhaskell.org/read/ https://hoogle.haskell.org/ (可以按type搜索函数) https://www.seas.upenn.edu/~cis194/fall16/index.html https://www.seas.upenn.edu/~cis552/15fa/schedule.html https://wiki.haskell.org/H-99:_Ninety-Nine_Haskell_Problems http://cheatsheet.codeslower.com/CheatSheet.pdf [**haskell ecosystem**](https://github.com/Gabriella439/post-rfc/blob/main/sotu.md) # How to run 在ghci里用 :load xxx.hs 加载脚本 long comment: {- -} https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using.html https://www.cis.upenn.edu/~cis1940/fall16/lectures/05-real-world-haskell.html # Basics 函数是最高优先级的运算符,注意括号 ```haskell >ghci succ 9 * 10 100 >ghci succ (9 * 10) 91 ``` 为了便于理解,函数也可以通过infix方式使用 ```haskell >ghci 92 `div` 10 >ghci 9 ``` 定义和调用函数 ```haskell >ghci doubleMe x = x + x >ghci doubleMe 9 18 >ghci doubleUs x y = x*2 + y*2 >ghci doubleUs 4 9 26 ``` Apostrophe doesn't have any special meaning in Haskell's syntax. It's a valid character to use in a function name [1,2,3] is just syntactic sugar for 1:2:3:[] 数组、字符串append: ++ 开头添加元素, 使用:, O(1) ```haskell ghci> 'A':" SMALL CAT" "A SMALL CAT" ghci> 5:[1,2,3,4,5] [5,1,2,3,4,5] ``` 数组索引 ```haskell ghci> [9.4,33.2,96.2,11.2,23.25] !! 1 33.2 ``` 数组比较:lexicographic顺序 ```haskell ghci> let a = [5,4,3,2,1] ghci> head a 5 ghci> tail a [4,3,2,1] ghci> last a -- 注意 1 ghci> init a -- 注意 [5,4,3,2] ghci> length a 5 ghci> reverse a --注意 [1,2,3,4,5] ghci> take 3 a [5,4,3] ghci> drop 2 a --注意 [3,2,1] ghci> minimum a 1 ghci> maximum a 5 ghci> sum a 15 ghci ghci> null [1,2,3] False ghci> null [] True ghci> sum [5,2,1,6,3,2,5,7] 31 ghci> product [6,2,1,2] 24 ghci> 4 `elem` [3,4,5,6] True ghci> 10 `elem` [3,4,5,6] False ``` ranges ```haskell ghci> [1..20] [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] ghci> ['a'..'z'] "abcdefghijklmnopqrstuvwxyz" ghci> ['K'..'Z'] "KLMNOPQRSTUVWXYZ" ghci> [2,4..20] [2,4,6,8,10,12,14,16,18,20] ghci> [3,6..20] [3,6,9,12,15,18] ``` cycle ```haskell ghci> take 10 (cycle [1,2,3]) [1,2,3,1,2,3,1,2,3,1] ghci> take 12 (cycle "LOL ") "LOL LOL LOL " ghci> take 10 (cycle [5]) [5,5,5,5,5,5,5,5,5,5] ghci> take 10 (repeat 5) [5,5,5,5,5,5,5,5,5,5] ``` list comprehension ```haskell ghci> [x*2 | x <- [1..10]] [2,4,6,8,10,12,14,16,18,20] ghci> [ x | x <- [50..100], x `mod` 7 == 3] [52,59,66,73,80,87,94] ``` fst,snd,zip ```haskell ghci> fst ("Wow", False) "Wow" ghci> snd ("Wow", False) False ghci> zip [1,2,3,4,5] [5,5,5,5,5] [(1,5),(2,5),(3,5),(4,5),(5,5)] ``` # Type/Typeclass The [Char] type is synonymous with String Integer: 大整数 Int: 32位整数 用":t"函数获取类型 ```haskell ghci> :t (True, 'a') (True, 'a') :: (Bool, Char) ghci> :t 4 == 5 4 == 5 :: Bool ghci> :t head head :: [a] -> a ghci> :t fst fst :: (a, b) -> a ghci> :t "asdsd" --字符串的类型是字符数组 "asdsd" :: [Char] ``` 函数类型声明, 如果使用ghci, 类型声明和定义应在一行, 用";"隔开 ```haskell removeNonUppercase :: [Char] -> [Char] removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']] ``` Typeclass. 查看一个类型遵从哪些typeclass可以用:info或者:i ```haskell ghci> :t (==) (==) :: (Eq a) => a -> a -> Bool ghci> :t (>) (>) :: (Ord a) => a -> a -> Bool ghci> :t read read :: (Read a) => String -> a ghci> :t show show :: Show a => a -> String ghci> :t minBound minBound :: Bounded a => a ghci> :t 20 20 :: (Num t) => t ``` Show的member可以用字符串表示. 用"show"函数. 反之从字符串读取用"read"函数. 类型不明的时候, 指定类型 ```haskell ghci> show 3 "3" ghci> show 5.334 "5.334" ghci> show True "True" ghci> read "8.2" + 3.8 12.0 ghci> read "5" - 2 3 ghci> read "[1,2,3,4]" ++ [3] [1,2,3,4,3] ghci> read "5" *** Exception: Prelude.read: no parse ghci> read "5" :: Int 5 ghci> read "5" :: Float 5.0 ghci> minBound :: Int -2147483648 ghci> maxBound :: Char '\1114111' ``` # Function Syntax ## case表达式格式 ```haskell case expression of pattern -> result pattern -> result pattern -> result ``` ```haskell head' :: [a] -> a head' xs = case xs of [] -> error "No head for empty lists!" (x:_) -> x --注意括号使用 -- The x:xs pattern is used a lot, especially with recursive functions ``` 定义多case函数时可以直接写多行,不用case表达。 调用时按照顺序从上到下扫描,所以**顺序matters**! 推论:定义递归函数时,base case必须在最上面 ```haskell sayMe :: (Integral a) => a -> String sayMe 1 = "One!" sayMe 2 = "Two!" sayMe 3 = "Three!" sayMe 4 = "Four!" sayMe 5 = "Five!" sayMe x = "Not between 1 and 5" ``` 模式匹配可能失败,所以需要catch all case放在最后 ```haskell charName :: Char -> String charName 'a' = "Albert" charName 'b' = "Broseph" charName 'c' = "Cecil" charName x = "unseen" --catch all term ``` ```haskell addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a) addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2) tell :: (Show a) => [a] -> String tell [] = "The list is empty" tell (x:[]) = "The list has one element: " ++ show x -- 可以写成 tell [x] tell (x:y:[]) = "The list has two elements: " ++ show x ++ " and " ++ show y -- 可以写成 tell [x,y] tell (x:y:_) = "This list is long. The first two elements are: " ++ show x ++ " and " ++ show y ``` 使用 name@ 来refer整体 ```haskell capital :: String -> String capital "" = "Empty string, whoops!" capital all@(x:xs) = "The first letter of " ++ all ++ " is " ++ [x] ``` ## guards 多条件检验: guards. 条件用pipe分割,最后otherwise. if-else, case, guards等价 https://stackoverflow.com/questions/9345589/guards-vs-if-then-else-vs-cases-in-haskell ```haskell --单参数 bmiTell :: (RealFloat a) => a -> String bmiTell bmi | bmi <= 18.5 = "You're underweight, you emo, you!" | bmi <= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!" | bmi <= 30.0 = "You're fat! Lose some weight, fatty!" | otherwise = "You're a whale, congratulations!" --多参数 bmiTell :: (RealFloat a) => a -> a -> String bmiTell weight height | weight / height ^ 2 <= 18.5 = "You're underweight, you emo, you!" | weight / height ^ 2 <= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!" | weight / height ^ 2 <= 30.0 = "You're fat! Lose some weight, fatty!" | otherwise = "You're a whale, congratulations!" --减少重复 bmiTell :: (RealFloat a) => a -> a -> String bmiTell weight height | bmi <= skinny = "You're underweight, you emo, you!" | bmi <= normal = "You're supposedly normal. Pffft, I bet you're ugly!" | bmi <= fat = "You're fat! Lose some weight, fatty!" | otherwise = "You're a whale, congratulations!" where bmi = weight / height ^ 2 skinny = 18.5 normal = 25.0 fat = 30.0 --上面的where也可以写成 where bmi = weight / height ^ 2 (skinny, normal, fat) = (18.5, 25.0, 30.0) --在where中定义函数 calcBmis :: (RealFloat a) => [(a, a)] -> [a] calcBmis xs = [bmi w h | (w, h) <- xs] where bmi weight height = weight / height ^ 2 ``` ## let语句 let语句本身是expression ```haskell ghci> 4 * (let a = 9 in a + 1) + 2 42 ghci> [let square x = x * x in (square 5, square 3, square 2)] [(25,9,4)] ghci> (let a = 100; b = 200; c = 300 in a*b*c, let foo="Hey "; bar = "there!" in foo ++ bar) (6000000,"Hey there!") ``` 在list comprehension中使用let ```haskell calcBmis :: (RealFloat a) => [(a, a)] -> [a] calcBmis xs = [bmi | (w, h) <- xs, let bmi = w / h ^ 2, bmi >= 25.0] ``` as-pattern:The @ symbol is used to give a name to a parameter and to match against a pattern after the @ symbol. ```haskell f (x:xs) = x:x:xs --可写为 f s@(x:xs) = x:s ghci> let a @ (b @ (Just c), Just d) = (Just 1, Just 2) in (a, b, c, d) ((Just 1,Just 2),Just 1,1,2) ``` # 自定义类型 等号后面的部份是value constructor. Value constructors的实质是函数, 返回类型是定义的类型. ```haskell data Bool = False | True data Shape = Circle Float Float Float | Rectangle Float Float Float Float --调用Circle或Rectangle创建出来的东西是Shape类型 ghci> :t Circle Circle :: Float -> Float -> Float -> Shape ghci> :t Rectangle Rectangle :: Float -> Float -> Float -> Float -> Shape ``` 注意,Shape是Type, 但Circle和Rectangle是函数不是Type. 所以不能定义为Circle->Float. ```haskell surface :: Shape -> Float -- use the constructor for pattern matching surface (Circle _ _ r) = pi * r ^ 2 surface (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1) ``` 如果直接调用Circle 10 20 5会报错,因为尚不支持show函数。末尾添加"deriving (Show)". ```haskell data Shape = Circle Float Float Float | Rectangle Float Float Float Float deriving (Show) ghci> Circle 10 20 5 Circle 10.0 20.0 5.0 ghci> Rectangle 50 230 60 90 Rectangle 50.0 230.0 60.0 90.0 ``` 由于value constructor是函数,所以可以partial调用,再用map ```haskell ghci> map (Circle 10 20) [4,5,6,6] [Circle 10.0 20.0 4.0,Circle 10.0 20.0 5.0,Circle 10.0 20.0 6.0,Circle 10.0 20.0 6.0] ``` 上面的定义可以重写为 ```haskell data Point = Point Float Float deriving (Show) data Shape = Circle Point Float | Rectangle Point Point deriving (Show) ``` ⚠️写函数参数的时候把constructor放进去!通过pattern matching定义多种行为。 ```haskell surface :: Shape -> Float surface (Circle _ r) = pi * r ^ 2 surface (Rectangle (Point x1 y1) (Point x2 y2)) = (abs $ x2 - x1) * (abs $ y2 - y1) ``` Record语法 ```haskell data Person = Person String String Int Float String String deriving (Show) firstName :: Person -> String firstName (Person firstname _ _ _ _ _) = firstname lastName :: Person -> String lastName (Person _ lastname _ _ _ _) = lastname age :: Person -> Int age (Person _ _ age _ _ _) = age height :: Person -> Float height (Person _ _ _ height _ _) = height phoneNumber :: Person -> String phoneNumber (Person _ _ _ _ number _) = number flavor :: Person -> String flavor (Person _ _ _ _ _ flavor) = flavor ``` 上面的可以用record语法简洁地表达出来。上列函数自动创建。 ```haskell data Person = Person { firstName :: String , lastName :: String , age :: Int , height :: Float , phoneNumber :: String , flavor :: String } deriving (Show) ``` 使用record语法后,创建新变量时使用named参数,顺序可以变 ```haskell data Car = Car {company :: String, model :: String, year :: Int} deriving (Show) ghci> Car {company="Ford", model="Mustang", year=1967} Car {company = "Ford", model = "Mustang", year = 1967} ``` 参数化是haskell的一种polymorphism机制。 ```haskell data IList = INil | ICons Int IList data CList = CNil | CCons Char CList data DList = DNil | DCons Double DList -- 可以写为 data List a = Nil | Cons a (List a) ``` ```haskell data Vector a = Vector a a a deriving (Show) vplus :: (Num t) => Vector t -> Vector t -> Vector t (Vector i j k) `vplus` (Vector l m n) = Vector (i+l) (j+m) (k+n) vectMult :: (Num t) => Vector t -> t -> Vector t (Vector i j k) `vectMult` m = Vector (i*m) (j*m) (k*m) scalarMult :: (Num t) => Vector t -> Vector t -> t (Vector i j k) `scalarMult` (Vector l m n) = i*l + j*m + k*n ``` 使用Emum ```haskell data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday deriving (Eq, Ord, Show, Read, Bounded, Enum) ghci> Wednesday Wednesday ghci> show Wednesday "Wednesday" ghci> read "Saturday" :: Day Saturday ghci> Saturday == Saturday True ghci> Saturday > Friday True ghci> Monday `compare` Wednesday LT ghci> succ Monday Tuesday ghci> pred Saturday Friday ghci> [Thursday .. Sunday] [Thursday,Friday,Saturday,Sunday] ghci> [minBound .. maxBound] :: [Day] [Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday] ``` 递归数据类型 ``` haskell -- Cons可以换成其他名字,如":-:" data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord) --infixr, infixl 定义fixity infixr 5 :-: data List a = Empty | a :-: (List a) deriving (Show, Read, Eq, Ord) --record语法 data List a = Empty | Cons { listHead :: a, listTail :: List a} deriving (Show, Read, Eq, Ord) ``` 例子:二叉搜索树 ```haskell data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq) singleton :: a -> Tree a singleton x = Node x EmptyTree EmptyTree treeInsert :: (Ord a) => a -> Tree a -> Tree a treeInsert x EmptyTree = singleton x treeInsert x (Node a left right) | x == a = Node x left right | x < a = Node a (treeInsert x left) right | x > a = Node a left (treeInsert x right) treeElem :: (Ord a) => a -> Tree a -> Bool treeElem x EmptyTree = False treeElem x (Node a left right) | x == a = True | x < a = treeElem x left | x > a = treeElem x right ``` # TypeClass 定义接口。 **instance** is for making our types instances of typeclasses. Eq的定义中使用了mutual recursion:简化了instance的实现. 否则只写一整套等于关系不够, 还得写一套不等于的. ```haskell class Eq a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool x == y = not (x /= y) x /= y = not (x == y) data TrafficLight = Red | Yellow | Green instance Eq TrafficLight where Red == Red = True Green == Green = True Yellow == Yellow = True _ == _ = False instance Show TrafficLight where show Red = "Red light" show Yellow = "Yellow light" show Green = "Green light" ``` 类型可以subcless, Num是Eq的subclass. 以下相当于class Num a where, only we state that our type a must be an instance of Eq. ```haskell class (Eq a) => Num a where ... ``` You can't have a function of the type a -> Maybe but you can have a function of a -> Maybe a or Maybe Int -> Maybe String ```haskell instance (Eq m) => Eq (Maybe m) where Just x == Just y = x == y Nothing == Nothing = True _ == _ = False ``` # 高阶函数 map pattern ```haskell shout::[Char]->[Char] shout [] = [] shout (c:cs) = toUpper c : shout cs squares :: [Int]->[Int] squares [] = [] squares (h:t) =(h*h):squares t --上面的两个函数明显有重复 mymap::(a -> b) -> [a] -> [b] mymap f [] = [] mymap f (c:cs) = f c : mymap f cs ``` reduction pattern ```haskell sumList ::[Int]->Int sumList [] = 0 --base case 0, operation + sumList (x:xs) = x + (sumList xs) catList ::[String]->String catList [] = "" --base case "", operation ++ catList (x:xs) = x ++ (catList xs) --上面的两个函数明显有重复 foo op b [] = b --b: base case foo op b (x:xs) = op x (foo op b xs) sumList' xs = foo (+) 0 xs catList' xs = foo (++) "" xs ``` 在haskell中,相同的函数的相同参数可以cancel ```haskell foo x y z = bar x y z --相当于 foo = bar --推论 shout = mymap toUpper squares = mymap (\h -> h * h) ``` partial函数调用的结果是函数 ```haskell multThree :: (Num a) => a -> a -> a -> a multThree x y z = x * y * z ghci> let multTwoWithNine = multThree 9 ghci> multTwoWithNine 2 3 54 ghci> let multWithEighteen = multTwoWithNine 2 ghci> multWithEighteen 10 180 ``` zipWith ```haskell ghci> zipWith' (+) [4,2,5,6] [2,6,2,3] [6,8,7,9] ghci> zipWith' max [6,3,2,1] [7,3,1,5] [7,3,2,5] ghci> zipWith' (++) ["foo ", "bar ", "baz "] ["fighters", "hoppers", "aldrin"] ["foo fighters","bar hoppers","baz aldrin"] ``` ⚠️:[foldr和foldl的区别](https://gist.github.com/CMCDragonkai/9f5f75118dda10131764) ```haskell foldr::(a->b->b)->b->[a]->b foldr op base [] = base foldr op base (x:xs) = op x (foldr op base xs) foldr op b (a1:a2:a3:a4:[]) => a1 `op` (a2 `op` (a3 `op` (a4 `op` b))) foldl op b (a1:a2:a3:a4:[]) => (((b `op` a1) `op` a2) `op` a3) `op` a4 --(((b `op` a4) `op` a3) `op` a2) `op` a1 --由于 a4 `op` b 的结果可以和a3结合, op的类型是a->b->b ``` foldl和foldl'的区别: seq forces its 1st argument to be evaluated, then returns its 2nd argument. It doesn't actually do anything with the 1st argument: seq exists solely as a way to force that value to be evaluated. http://book.realworldhaskell.org/read/functional-programming.html#id594142 ```haskell foldl f base [] = base foldl f base (x:xs) = let base' = base `f` x in foldl f base' xs foldl' f base [] = base foldl' f base (x:xs) = let base' = base `f` x in seq base' $ foldl' f base' xs ghci> :type seq seq :: a -> t -> t ``` scanl和scanr类似foldl/foldr,但是输出中间结果 ```haskell ghci> scanl (+) 0 [3,5,2,1] [0,3,8,10,11] ghci> scanr (+) 0 [3,5,2,1] [11,8,3,1,0] ghci> scanl1 (\acc x -> if x > acc then x else acc) [3,4,5,3,7,9,2,1] [3,4,5,5,7,9,9,9] ghci> scanl (flip (:)) [] [3,2,1] [[],[3],[2,3],[1,2,3]] ``` $: function application ```haskell ghci> :t ($) ($) :: (a -> b) -> a -> b ghci> sqrt $ 3 + 4 + 9 --右侧的表达式被用于左侧函数的参数 4.0 ghci> ($ 3) (4+) 7 ghci> ($) (4+) 3 7 ghci> (4+) $ 3 7 ghci> map ($ 3) [(4+), (10*), (^2), sqrt] [7.0,30.0,9.0,1.7320508075688772] ``` dot: 复合函数 ```haskell ghci> map (\x -> negate (abs x)) [5,-3,-6,7,-3,2,-19,24] [-5,-3,-6,-7,-3,-2,-19,-24] ghci> map (negate . abs) [5,-3,-6,7,-3,2,-19,24] [-5,-3,-6,-7,-3,-2,-19,-24] ghci> map (negate . sum . tail) [[1..5],[3..6],[1..7]] [-14,-15,-27] fn x = ceiling (negate (tan (cos (max 50 x)))) fn = ceiling . negate . tan . cos . max 50 ``` map是fmap的特例 ```haskell ghci> :t fmap fmap :: Functor f => (a -> b) -> f a -> f b ghci> :t map map :: (a -> b) -> [a] -> [b] ``` https://stackoverflow.com/questions/13134825/how-do-functors-work-in-haskell # IO 类似于Recipe, 描述如何产生类型a的data, 通过Main函数执行后得到data. do语句组合多个recipe to get the value out of an I/O action, you have to perform it inside another I/O action by binding it to a name with <-. 通过左箭头命名Recipe的结果以使用. putStrLn的输入是String,返回结果类型是()的I/O动作。()是空tuple。空tuple的值和类型都是()。 ```haskell ghci> :t putStrLn putStrLn :: String -> IO () ghci> :t putStrLn "hello, world" putStrLn "hello, world" :: IO () ``` 空IO: ```haskell return () ``` sequence ```haskell main = do a <- getLine b <- getLine c <- getLine print [a,b,c] --sequence :: [IO a] -> IO [a] --上面的相当于 main = do rs <- sequence [getLine, getLine, getLine] print rs ghci> sequence (map print [1,2,3]) 1 2 3 [(),(),()] ``` mapM: map和sequence二合一. ⚠️[mapM和mapM_的区别](https://stackoverflow.com/questions/27609062/what-is-the-difference-between-mapm-and-mapm-in-haskell/27609146)。 ```haskell ghci> mapM print [1,2,3] 1 2 3 [(),(),()] ghci> mapM_ print [1,2,3] 1 2 3 ``` print的输入是Show类型的参数,相当于putStrLn . show ```haskell show :: Show a => a -> String print :: Show a => a -> IO () ``` # Functor, Applicative http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html ## Functor 应用函数到wrapped值。 <$>是fmap的infix版本。 https://hackage.haskell.org/package/base-4.14.0.0/docs/Data-Functor.html#t:Functor ```haskell fmap:: (a->b)->t a->t b (<$>) :: (Functor f) => (a -> b) -> f a -> f b f <$> x = fmap f x ``` ## Applicative wrapped函数应用到wrapped值。 https://hackage.haskell.org/package/base-4.14.0.0/docs/Control-Applicative.html#g:1 [Do Notation Equivalent](https://simonmar.github.io/bib/papers/applicativedo.pdf) <*>在Control.Applicative中定义。 pure: takes a value and puts it in a minimal default context that still holds that value ```haskell class (Functor f) => Applicative f where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b ``` ## Applicative例子:Maybe ```haskell instance Applicative Maybe where pure = Just Nothing <*> _ = Nothing (Just f) <*> something = fmap f something ghci> (Just (+3) <*> Just 2) == (Just 5) True ghci> [(*2), (+3)] <*> [1, 2, 3] [2, 4, 6, 4, 5, 6] ghci> (*) <$> Just 5 <*> Just 3 -- (fmap (*) Just 5) <*> Just 3) Just 15 ghci> (/) <$> Just 5 <*> Just 3 Just 1.6666666666666667 ghci> Just (/2) <*> Just 5 Just 2.5 ghci> Just (2/) <*> Just 5 Just 0.4 ``` ## Applicative例子:列表 ``` haskell instance Applicative [] where pure x = [x] fs <*> xs = [f x | f <- fs, x <- xs] --所有函数 map 到所有值, f1 map完所有值, f2再map所有值... ghci> [(*0),(+100),(^2)] <*> [1,2,3] [0,0,0,101,102,103,1,4,9] ghci> [(+),(*)] <*> [1,2] <*> [3,4] [4,5,5,6,3,4,6,8] -- 相当于[(+1),(+2),(*1),(*2)] <*> [3,4] ghci> (++) <$> ["ha","heh","hmm"] <*> ["?","!","."] ["ha?","ha!","ha.","heh?","heh!","heh.","hmm?","hmm!","hmm."] ``` Applicative style和list comprehension的关系 ```haskell ghci> [ x*y | x <- [2,5,10], y <- [8,10,11]] [16,20,22,40,50,55,80,100,110] ghci> (*) <$> [2,5,10] <*> [8,10,11] [16,20,22,40,50,55,80,100,110] ghci> filter (>50) $ (*) <$> [2,5,10] <*> [8,10,11] [55,80,100,110] ``` ## Applicative例子:IO ```haskell instance Applicative IO where pure = return a <*> b = do f <- a x <- b return (f x) myAction :: IO String myAction = (++) <$> getLine <*> getLine ``` ```haskell liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c liftA2 f a b = f <$> a <*> b ghci> liftA2 (:) (Just 3) (Just [4]) Just [3,4] ghci> (:) <$> Just 3 <*> Just [4] Just [3,4] ``` ```haskell sequenceA :: (Applicative f) => [f a] -> f [a] sequenceA [] = pure [] sequenceA (x:xs) = (:) <$> x <*> sequenceA xs ghci> sequenceA [Just 3, Just 2, Just 1] Just [3,2,1] ghci> sequenceA [Just 3, Nothing, Just 1] Nothing ghci> sequenceA [(+3),(+2),(+1)] 3 [6,5,4] ghci> sequenceA [[1,2,3],[4,5,6]] [[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]] ghci> sequenceA [[1,2,3],[4,5,6],[3,4,4],[]] [] ``` # Monad Monad: 应用返回wrapped值的函数到wrapped值。>>=类似于pipeline chaining操作。Monad都是applicative functor, even if the Monad class declaration doesn't say so. http://ucsd-pl.github.io/cse230/lectures/lec-monads.html ```haskell class Monad m where return :: a -> m a --和pure一个东西,就名字不同. takes something and wraps it in a monad (>>=) :: m a -> (a -> m b) -> m b --bind, it takes a value with a context and feeds it to a function that takes a normal value but returns a monadic value. (>>) :: m a -> m b -> m b --never implement it when making Monad instances x >> y = x >>= \_ -> y fail :: String -> m a --never use it explicitly in our code fail msg = error msg ``` ## Maybe Monad ```haskell instance Monad Maybe where return x = Just x Nothing >>= f = Nothing Just x >>= f = f x fail _ = Nothing ghci> return "WHAT" :: Maybe String Just "WHAT" ghci> Just 9 >>= \x -> return (x*10) --x取脱掉Just的值 Just 90 ghci> Nothing >>= \x -> return (x*10) Nothing ``` ## Do Notation not just for IO. In a do expression, every line is a monadic value ```haskell e1 >>= \v1 -> e2 >>= \v2 -> e3 >>= \v3 -> e ---相当于 do v1 <- e1 v2 <- e2 v3 <- e3 e do {v1 <- e1;v2<- e2; v3<- e3; e} ``` ```haskell foo :: Maybe String foo = Just 3 >>= (\x -> Just "!" >>= (\y -> Just (show x ++ y))) foo :: Maybe String foo = do x <- Just 3 y <- Just "!" Just (show x ++ y) ``` ```haskell ghci> Just 9 >>= (\x -> Just (x > 8)) Just True marySue :: Maybe Bool marySue = do x <- Just 9 Just (x > 8) ``` ## List Monad ```haskell instance Monad [] where return x = [x] xs >>= f = concat (map f xs) ---拼一起 fail _ = [] --行为类似for循环 ghci> [3,4,5] >>= \x -> [x,-x] [3,-3,4,-4,5,-5] ghci> [1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch) [(1,'a'),(1,'b'),(2,'a'),(2,'b')] listOfTuples :: [(Int,Char)] listOfTuples = do n <- [1,2] ch <- ['a','b'] return (n,ch) ghci> [ (n,ch) | n <- [1,2], ch <- ['a','b'] ] [(1,'a'),(1,'b'),(2,'a'),(2,'b')] ``` ## State Monad http://brandon.si/code/the-state-monad-a-tutorial-for-the-confused/ https://wiki.haskell.org/State_Monad ```haskell newtype State s a = State { runState :: s -> (a,s) } instance Monad (State s) where return x = State $ \s -> (x,s) (State h) >>= f = State $ \s -> let (a, newState) = h s (State g) = f a in g newState get = \s -> (s, s) -- 不改变当前状态,得到当前状态 用法:s<-get put s = \_ -> ((), s) -- 替换状态 modify f = \s -> ((), f s) -- Update the state runState :: State s a -> s -> (a, s) runState t s = t s evalState :: State s a -> s -> a execState :: State s a -> s -> s ``` ```haskell type Stack = [Int] pop :: Stack -> (Int,Stack) pop (x:xs) = (x,xs) push :: Int -> Stack -> ((),Stack) push a xs = ((),a:xs) stackManip :: Stack -> (Int, Stack) stackManip stack = let ((),newStack1) = push 3 stack (a ,newStack2) = pop newStack1 in pop newStack2 ``` 可以重写为 ```haskell import Control.Monad.State pop :: State Stack Int pop = State $ \(x:xs) -> (x,xs) push :: Int -> State Stack () push a = State $ \xs -> ((),a:xs) stackManip :: State Stack Int stackManip = do push 3 a <- pop pop ``` ## Parser Monad http://hackage.haskell.org/package/polyparse-1.13/docs/Text-ParserCombinators-Poly-Parser.html#t:Parser https://www.seas.upenn.edu/~cis552/15fa/lectures/Parsers.html https://hackage.haskell.org/package/parsec-3.1.14.0/docs/Text-ParserCombinators-Parsec-Char.html http://dev.stephendiehl.com/fun/002_parsers.html https://hackage.haskell.org/package/polyparse-1.13/docs/src/Text.ParserCombinators.HuttonMeijer.html#many1 ```haskell (>>=) :: Parser t a -> (a -> Parser t b) -> Parser t b (>>) :: Parser t a -> Parser t b -> Parser t b return :: a -> Parser t a fail :: String -> Parser t a ``` https://wiki.haskell.org/Parsing_a_simple_imperative_language a <|> b will first try parser a and if it fails (but without actually consuming any input) then parser b will be used ```haskell many1 :: Stream s m t => ParsecT s u m a -> ParsecT s u m [a]Source# --many1 p applies the parser p one or more times. Returns a list of the returned values of p. word = many1 letter ``` 使用mapM(map+sequence)进行parsing. ```haskell string::String->Parser string string cs = mapM char cs ``` orElse <|>: 如果第一个parser失败,则不consume字符串,重新run第二个parser ```haskell orElse :: Parser a -> Parser a -> Parser a orElse p1 p2 = P (\cs -> case runParser p1 cs of [] -> runParser p2 cs r1s -> r1s ) ``` parsing failure和return []的区别 ``` \s -> [] -- FAILURE \s -> [([], s)] -- return [] ``` ## Either ```haskell instance (Error e) => Monad (Either e) where return x = Right x -- 注意类型是Right Right x >>= f = f x Left err >>= f = Left err fail msg = Left (strMsg msg) ``` 错误处理 ```haskell evalEither :: Expr -> Either Expr Int evalEither = eval where eval (Number n) = return n eval (Plus e1 e2) = do n1 <- eval e1 n2 <- eval e2 return (n1 + n2) eval (Div e1 e2) = do n1 <- eval e1 n2 <- eval e2 if n2 /= 0 then return (n1 `div` n2) else throwAnError e2 eval (Def e n) = tryCatchError -- try (eval e) -- { e }. --evaluator (\exn -> return n) -- (exn) { ...} --error handler throwAnError = Left tryCatchError :: Either e a -> (e -> Either e a) -> Either e a --second argument: error handler. if succeeds, produce Either e a type, so the handler has to do the same tryCatchError (Right val) handler = Right val tryCatchError (Left exn) handler = handler exn ``` ```haskell class (Monad m) => MonadError e m | m -> e where throwError :: e -- error to throw -> m a catchError :: m a -- action to execute -> (e -> m a) -- error handler -> m a ``` ## transformer ``` -- >>> :t runExceptT -- runExceptT :: ExceptT e m a -> m (Either e a) -- >>> :t runStateT -- runStateT :: StateT s m a -> s -> m (a, s) ``` ## Random Generator https://hackage.haskell.org/package/QuickCheck-2.13.2/docs/Test-QuickCheck-Gen.html Gen是Monad ```haskell elems::[a]->Gen a elems xs = do i <- choose (0, length xs) return (xs !! i) posPr::do posPr = do x <- pos y <- pos return (x, y) randomThings::(Arbutrary a) => IO [a] randomThings = sample' arbitrary ``` ## 常用函数 liftM和fmap十分接近 ```haskell fmap :: (Functor f) => (a -> b) -> f a -> f b liftM :: (Monad m) => (a -> b) -> m a -> m b liftM :: (Monad m) => (a -> b) -> m a -> m b liftM f m = m >>= (\x -> return (f x)) liftM :: (Monad m) => (a -> b) -> m a -> m b liftM f m = do x <- m return (f x) ``` ap类似<*> ```haskell (<*>) :: (Applicative f) => f (a -> b) -> f a -> f b ap :: (Monad m) => m (a -> b) -> m a -> m b ap mf m = do f <- mf x <- m return (f x) ``` join ```haskell join :: (Monad m) => m (m a) -> m a join mm = do m <- mm m ghci> join (Just (Just 9)) Just 9 ghci> join [[1,2,3],[4,5,6]] [1,2,3,4,5,6] ghci> runState (join (State $ \s -> (push 10,1:2:s))) [0,0,0] ((),[10,1,2,0,0,0]) ``` filterM ```haskell filter :: (a -> Bool) -> [a] -> [a] filterM :: (Monad m) => (a -> m Bool) -> [a] -> m [a] ``` foldM ```haskell foldl :: (a -> b -> a) -> a -> [b] -> a foldM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a binSmalls :: Int -> Int -> Maybe Int binSmalls acc x | x > 9 = Nothing | otherwise = Just (acc + x) ghci> foldM binSmalls 0 [2,8,3,1] Just 14 ghci> foldM binSmalls 0 [2,11,3,1] Nothing ``` ## Monad Laws return x >>= f is the same damn thing as f x ```haskell ghci> return 3 >>= (\x -> Just (x+100000)) Just 100003 ghci> (\x -> Just (x+100000)) 3 Just 100003 ghci> return "WoM" >>= (\x -> [x,x,x]) ["WoM","WoM","WoM"] ghci> (\x -> [x,x,x]) "WoM" ["WoM","WoM","WoM"] ``` m >>= return is no different than just m ```haskell ghci> Just "move on up" >>= (\x -> return x) Just "move on up" ghci> [1,2,3,4] >>= (\x -> return x) [1,2,3,4] ghci> putStrLn "Wah!" >>= (\x -> return x) Wah! ``` Doing (m >>= f) >>= g is just like doing m >>= (\x -> f x >>= g) ```haskell ghci> return (0,0) >>= landRight 2 >>= landLeft 2 >>= landRight 2 Just (2,4) ghci> ((return (0,0) >>= landRight 2) >>= landLeft 2) >>= landRight 2 Just (2,4) ``` composition f <=< (g <=< h) should be the same as (f <=< g) <=< h ```haskell (.) :: (b -> c) -> (a -> b) -> (a -> c) f . g = (\x -> f (g x)) (<=<) :: (Monad m) => (b -> m c) -> (a -> m b) -> (a -> m c) f <=< g = (\x -> g x >>= f) ghci> let f x = [x,-x] ghci> let g x = [x*3,x*2] ghci> let h = f <=< g ghci> h 3 [9,-9,6,-6] ``` # module ```haskell Map.fromList :: (Ord k) => [(k, v)] -> Map.Map k v ghci> Map.fromList [("betty","555-2938"),("bonnie","452-2928"),("lucille","205-2928")] fromList [("betty","555-2938"),("bonnie","452-2928"),("lucille","205-2928")] ghci> Map.fromList [(1,2),(3,4),(3,2),(5,5)] fromList [(1,2),(3,2),(5,5)] --重复被扔掉 ghci> Map.empty fromList [] ghci> Map.insert 3 100 Map.empty fromList [(3,100)] ghci> Map.insert 5 600 (Map.insert 4 200 ( Map.insert 3 100 Map.empty)) fromList [(3,100),(4,200),(5,600)] ghci> Map.insert 5 600 . Map.insert 4 200 . Map.insert 3 100 $ Map.empty fromList [(3,100),(4,200),(5,600)] ``` ###### tags: `language`