Haskell 学习总结

2018/05/26 Haskell

GHCi

启动 GHCi, 在 Mac OS 上可以使用 stack 工具: stack ghci

-- Haskell 的源码文件使用 .hs 后缀,内容全部是源码
-- Literate Haskell 的源文件使用 .lhs 后缀,>开头后接一个空格的才算源码
-- 加载源码文件到 GHCi
:load file.hs
:l file.hs
-- 重新加载有修改的源码文件到 GHCi
:reload file.hs
:r file.hs
-- 执行多行语句
:{
let addTwo :: Int -> Int -> Int
    addTwo x y = x + y 
:}
-- 查看类型
:type 1
:t 1

基础语法

-- 单行注释使用 -- 开头
{-
    注释块使用 {--}
-}
-- 源文件中声明
x = 5     -- x is 5
answer = 2 * {-
    block comment
-} 3 {- inline comment -}
r = 5.0
-- GHCi 中声明变量
let r = 5.0
-- 分支语法
mySignum x = if x < 0 then -1 else 1
-- case of 语法
ex03 = case "Hello" of
    [] -> 3
    ('H':s) -> length s
    _ -> 7

函数

函数即是给定参数然后返回结果的东西,Haskell 中函数如下示例:

-- 函数名 参数列表 = 函数体
area r = pi * r ^ 2
-- 使用,参数实际值使用空格分割
area 5
-- 多参函数
areaRect l w = l * w
-- 函数组合
areaSquare s = areaRect s s
-- 模式匹配,调用时从上往下匹配到则直接返回
sumtorial :: Integer -> Integer
sumtorial 0 = 0
sumtorial n = n + sumtorial (n-1)
-- Guards 模式
absolute x
    | x < 0 = 0 - x -- 注意 - 也是一个函数, 必须使用两个参数
    | otherwise = x -- otherwise 始终是 True
-- 模式匹配和 Guards 模式可以一起使用
foo :: Integer -> Integer
foo 0 = 16
foo 1 
  | "Haskell" > "C++" = 3
  | otherwise         = 4
foo n
  | n < 0            = 0
  | n `mod` 17 == 2  = -43
  | otherwise        = n + 3
-- where 绑定变量
heron a b c = sqrt (s * (s - a) * (s - b) * (s - c))
    where s = (a + b + c) / 2
-- let in 绑定变量
roots a b c =
    let sdisc = sqrt (b * b - 4 * a * c)
        twice_a = 2 * a
    in ((-b + sdisc) / twice_a, (-b - sdisc) / twice_a)
-- where 和 Guards 组合到一起
numOfRealSolution a b c
    | disc > 0 = 2
    | disc == 0 = 1
    | otherwise = 0
        where
        disc = b ^ 2 - 4 * a * c
-- 函数的优先级始终比中缀操作符高
-- 会被识别为 (f 3 n) + (1 7)
f 3 n+1 7
-- 多参函数可以看做接收一个参数,返回一个函数
sum a b = a + b
addOne = sum 1

类型

Haskell 中所有类型都是以大写字母开头,所有变量都有类型。

Prelude> :t 'H'
'H' :: Char
Prelude> :t "Hello"
"Hello" :: [Char]

函数也有自己的类型,如下表示接收一个 Bool 然后返回一个 Bool

Prelude> :t not
not :: Bool -> Bool

定义函数时可以先声明其类型,不声明的话编译器会自动推断其类型:

xor :: Bool -> Bool -> Bool
xor p q = (p || q) && not (p && q)

常用类型:

类型 含义
Int 整数,有限
Integer 整型,无限
Double 浮点数
Bool 布尔值
(Type1,Type2…) 元组,Type1 Type2 是具体的类型,可以不同
[Type] 列表,Type 是具体的类型
Char 单个字符,值使用 ‘’ 括起来
String 字符串,[Char]的别名,使用 “” 括起来

类型定义使用 data 关键字,枚举列出类型所有的构造子,构造子像函数样被调用后会创建类型的值

data FailableDouble = Failure
            | OK Double
    deriving Show

-- 使用构造子创建类型的值
ex01 = Failure
ex02 = Ok 3.4
-- 使用构造子模式匹配
failureToZero :: FailableDouble -> Double
failureToZero Failure = 0
failureToZero (OK d) = d
-- 模式匹配的可选格式
-- pat ::= _
--     | var
--     | var@(pat)
--     | (cONSTRUCTOR pat1 pat2 ... path)
-- 类型定义可以使用递归, 也就是构造子参数中使用本类型
data IntList = Empty | Cons Int IntList
-- 使用泛型定义类型
data List t = E | C t (List t)

类型类

类型类类似于 Java 中的接口,他声明了一系列函数,类型可以实现这些函数,这样就可以将实际类型的值传给这些函数以实现类似泛型的功能。

比如 (+) 函数实际支持的就是 Num 类型类:

Prelude> :t (+)
(+) :: Num a => a -> a -> a
-- 1 是一个 Num, 但是他的具体类型会在用到的时候才确定
Prelude> :type 1
1 :: Num p => p
Prelude>  1 + 1
2
Prelude> :type 1.1
-- Fractional 是 Num 的子类型类
1.1 :: Fractional p => p
Prelude> 1.1 + 1.1
2.2
-- 1 是一个 Num,而 1.1 是一个 Fractional
-- 编译器会首先推断出 1.1 是一个 Double 类型(Fractional 下的类型)
-- 然后 1 也会被推断成 Double
Prelude> 1 + 1.1
2.1

Functor, Applicative, Monad 类型类:

--Functor:
    class Functor F where
    --F为类型构造子,a,b为类型参数, F a为具体类型,下同
    fmap :: (a -> b) -> F a -> F b
    --在源代码中看到用了forall来实现fmap,forall的意义?(在hayoo中没有搜到相关的函数)
--laws:
    fmap id = id
    fmap (g . h) = fmap g . fmap h
--instance:
    class Functor F => Pointed F where
    pure :: a -> F a
    --laws:
    fmap g . pure=pure . g
-- **********************************************
--Applicative:
    class Functor F => Applicative F where
    pure  :: a -> F a
    (<*>) :: F (a -> b) -> F a -> F b
    --问题:能够直接在程序中使用<*>吗,还是需要给出具体实现
    --如果能的话,那么<*>是由谁实现的,在哪里实现的,是如何实现的?
    --一个想法:
        --此处的函数声明仅仅提供了接口,如果一个类想成为类型类的实例必须提供对应函数的实现
        --因此一个类型如果是该类型类的实现,那么这些函数可以直接用
--Laws:
    fmap g x=pure g <*> x
--Pointed=>Applicative':
    class Pointed F => Applicative' F where
    (<*>) :: F (a -> b) -> F a -> F b
-- **********************************************
--Monad:
    class Monad M where
    return :: a -> M a
    (>>=):: M a -> (a -> M b) -> M b
    (>>):: M a -> M b -> M b
    M >> n = M >>= \_ -> n
    fail:: String -> M a
--Applicative=> Monad':
    class Applicative M => Monad' M where
    (>>=) :: M a -> (a -> M b) -> M b
instance Monad Maybe where
    return = Just
    (Just x) >>= g = g x
    Nothing  >>= _ = Nothing
class Applicative M => Monad' M where
    join :: M (M a) -> M a
--The Monad Laws:
    return a >>= k = k a
    M >>= return   = M
    M >>= (\x -> k x >>= h) = (M >>= k)
    fmap F xs = xs >>= return . F = liftM F xs
--another discription:
    return >=> g = g
    g >=> return = g
    (g >=> h) >=> k  =  g >=> (h >=> k)
--MonadTrans:
    class MonadTrans t where 
    lift :: Monad M => M a -> t M a
-- **********************************************
--Monoid:
    class Monoid a where
    mempty  :: a
    mappend :: a -> a -> a
    mconcat :: [a] -> a
    mconcat = foldr mappend mempty
-- **********************************************
--Foldable:
class Foldable T where
    fold:: Monoid M => T M -> M
    foldMap :: Monoid M => (a -> M) -> T a -> M
 
    foldr   :: (a -> b -> b) -> b -> T a -> b
    foldl   :: (a -> b -> a) -> a -> T b -> a
    foldr1  :: (a -> a -> a) -> T a -> a
    foldl1  :: (a -> a -> a) -> T a -> a
instance Foldable [] where
    foldMap g = mconcat . map g
 
data Tree a = Empty | LeaF a | Node (Tree a) a (Tree a)
    instance Foldable Tree where
    foldMap F Empty = mempty
    foldMap F (Leaf x) = F x
    foldMap F (Node l k r) = foldMap F l ++ F k ++ foldMap F r
    where (++) = mappend
    
-- **********************************************
--Traversable:
class (Functor T, Foldable T) => Traversable T where
    traverse  :: Applicative F => (a -> F b) -> T a -> F (T b)
    sequenceA :: Applicative F => T (F a) -> F (T a)
    mapM      :: Monad M => (a -> M b) -> T a -> M (T b)
    sequence  :: Monad M => T (M a) -> M (T a)
-- **********************************************
--Category:
class Category (~>) where
    id  :: a ~> a
    (.) :: (b ~> c) -> (a ~> b) -> (a ~> c)
    
-- **********************************************
--Arrow:
class Category (~>) => Arrow (~>) where
    arr :: (b -> c) -> (b ~> c)
    first :: (b ~> c) -> ((b, d) ~> (c, d))
    second :: (b ~> c) -> ((d, b) ~> (d, c))
    (***) :: (b ~> c) -> (b' ~> c') -> ((b, b') ~> (c, c'))
    (&&&) :: (b ~> c) -> (b ~> c') -> (b ~> (c, c'))
--Laws:
    arr id = id
    arr (h . g) = arr g >>> arr h
    first (arr g) = arr (g *** id)
    first (g >>> h) = first g >>> first h
    first g >>> arr (id *** h) = arr (id *** h) >>> first g
    first g >>> arr fst = arr fst >>> g
    first (first g) >>> arr assoc = arr assoc >>> first g
    assoc ((x,y),z) = (x,(y,z))

Search

    Table of Contents