Haskell在脑海里的踪迹
基于范畴理论,在每一个范畴里面都包含一个objects和fmap关系,利用functor映射到不同的category。 可以继续思考的是一个fmap:(*->*) 一个fmap是可以充当形参的,因为他正如在一个List module中 一个一个的破折号和箭头构成的map关系被放进List这个category中。
module List (
elemIndex, elemIndices,
find, findIndex, findIndices,
nub, nubBy, delete, deleteBy, (\\), deleteFirstsBy,
union, unionBy, intersect, intersectBy,
intersperse, transpose, partition, group, groupBy,
inits, tails, isPrefixOf, isSuffixOf,
mapAccumL, mapAccumR,
sort, sortBy, insert, insertBy, maximumBy, minimumBy,
genericLength, genericTake, genericDrop,
genericSplitAt, genericIndex, genericReplicate,
zip4, zip5, zip6, zip7,
zipWith4, zipWith5, zipWith6, zipWith7,
unzip4, unzip5, unzip6, unzip7, unfoldr,
-- ...and what the Prelude exports
-- []((:), []), -- This is built-in syntax
map, (++), concat, filter,
head, last, tail, init, null, length, (!!),
foldl, foldl1, scanl, scanl1, foldr, foldr1, scanr, scanr1,
iterate, repeat, replicate, cycle,
take, drop, splitAt, takeWhile, dropWhile, span, break,
lines, words, unlines, unwords, reverse, and, or,
any, all, elem, notElem, lookup,
sum, product, maximum, minimum, concatMap,
zip, zip3, zipWith, zipWith3, unzip, unzip3
) where
我们所看到
data Bool = False | True deriving
(Read, Show, Eq, Ord, Enum, Bounded)
data Int = -65532|...|-1|0|1|2|...|65532
data Integer = ...-2|-1|0|1|2|3|4|...
从这边我们可以看到False ,True, 1,2,3,4….叫做Data constructor 而Bool,Integer叫做Type Constructor,所以你完全可以把类型当作数据,而这个类型包含表达式、函数、数据。
类也包含
- 原子类型 Integer Char Integer->Char(functions mapping from Integer to Char)
- 结构类型 [Integer] 数字列表 (Integer,Char)数字字符tuple
比如:
1::Int
inc::Integer->Integer
[1]::[Integer]
('b',4)::[Char,Integer]
::可以读作 has type类型为
我们也会看到很多类的继承
class Foo a => Bar a where ...
instance (Eq a, Show a) => Foo [a] where ...
instance Num a => Bar [a] where ...
class (Eq a) => Ord a where
(<),(<=),(>=),(>)::a->a->Bool
max,min ::a->a->a
----此时Eq叫做Ord的子类
其实更多的可以参考Haskell 98 Report
当然你事先得知道类的定义
class cx => C u where { cbody }
class 方法被叫做haskell里的顶层声明。
而有一些比较有趣的lambda转换
\x->\y->(+) x y ---> \x y -> (+) x y
(x+) --> \y->x+y
(+y) --> \x->x+y
(+) --> \x y -> x+y
(.) :: (b->c) ->(a-b)->(a->c)
f.g = \x-> f (g x)
在你编写函数的时候: 一个数据组和可以变为(x:xs) == x+xs完整的数据组和 ,只不过x代表这个数据组和的head部分,而xs表示tail部分 而如果 s@(x:xs) 其中的s代表就是整个数据组和了,这是一个不错的技巧。
f (x:xs) = x❌xs
f s@(x:xs) = x:s
当然你也可能遇到Wild-cards情况
head (x:_) = x
head (_:xs) = xs
另外一个分支关于模式匹配的相似效果是case定义 f p11 … p1k = e1 … f pn1 … pnk = en 其中pij是模式组合,
如果你使用case进行书写
f x1 x2 ... xk = case (x1,...,xk) of (p11,...,p1k) -> e1
...
(pn1,...,pnk)->en
比如下面:
--采用模式匹配方式
take _ [] = []
take 0 [] = []
take n (x:xs) = x:take (n-1) xs
--采用case方式
take m ys = case (m,ys) of
(0,_) -> []
(_,[]) -> []
(n,x:xs) -> x:take (n-1) xs
一个比较重要的functor的定义,因为它用于范畴之间的链接,主要是一个Functor类的实现和fmap函数的定义
class Functor f where
fmap :: (a->b) -> f a->f b
对应的Functor实例是
instance Functor Tree where
fmap f (Leaf x) = Leaf (f x)
fmap f (Branch t1 t2) = Branch (f t1) (f t2)
之所以写上这个你会发现,我们使用
Date Tree a= Leaf a| Branch a a
只是定义了类型,但是该类型(或者说大点,叫做范畴)并没有定义fmap,基本上所有的calss类型定义完之后都需要使用instance Functor进行fmap的
定义,这样对象和映射的组合才够成了范畴。
我们可能一直在用,但是并未知晓。当然上面需要特别注意的是实例声明额时候使用的是Tree,而不是Tree a. 也就是haskell和c++\java不同的是类型变量和methods的定义是分开的。 Haskell类类似于java的interface,定义的是一个协议,而不是一个定义一个对象本身。而且最重要的一个区别是haskell没有访问控制 修饰符比如public 、private。取而代之的是使用module模块系统用来隐藏和显示一个类的组成。
其实写到这边,模块系统你可能有所理解,当然模式匹配在函数定义中也是训练的很多,参考Haskell库文档结构 但是关于instance,你接触、练习还是有点少,所以这部分内容可以继续探索,包括IOMonad(一个新的IO范畴,可以当做一种抽象数据类型) 也包括图形化的制作等。