题
有什么鲜为人知,但很有用的特色Haskell编程语言。(我懂的语言本身是不太知名的,但和我一起工作。甚至解释简单的事情,在Haskell,如限定的斐波那契数列与一行代码,会投赞成票通过我。)
- 尝试限制的答案的核心Haskell
- 一个特征每答案
- 举一个例子,并简短说明的功能,不仅仅是一个链接到的文件
- 标签的功能使用大胆的标题作为第一线
解决方案
我的脑子就炸
如果你试图编纂这个代号:
{-# LANGUAGE ExistentialQuantification #-}
data Foo = forall a. Foo a
ignorefoo f = 1 where Foo a = f
您将得到这一错误信息:
$ ghc Foo.hs Foo.hs:3:22: My brain just exploded. I can't handle pattern bindings for existentially-quantified constructors. Instead, use a case-expression, or do-notation, to unpack the constructor. In the binding group for Foo a In a pattern binding: Foo a = f In the definition of `ignorefoo': ignorefoo f = 1 where Foo a = f
其他提示
用户定义控制结构
Haskell没有速记三操作员。内置 if
-then
-else
总是三元,并表(必要的语言往往有 ?:
=expression, if
=的声明).如果你想,不过,
True ? x = const x
False ? _ = id
将定义 (?)
是三操作员:
(a ? b $ c) == (if a then b else c)
你会有助于宏大多数其他语言来定义自己的短路逻辑运算符,但Haskell是一个完全懒惰的语言,所以它只是工作。
-- prints "I'm alive! :)"
main = True ? putStrLn "I'm alive! :)" $ error "I'm dead :("
吸引人
吸引人的是你的朋友。我承认,这不是部分的"核心",所以 cabal install hoogle
现在你知道如何"如果你是在寻找一个更高阶功能,它已经存在"(ephemient的评论).但是你怎么找到这的功能?与吸引人!
$ hoogle "Num a => [a] -> a"
Prelude product :: Num a => [a] -> a
Prelude sum :: Num a => [a] -> a
$ hoogle "[Maybe a] -> [a]"
Data.Maybe catMaybes :: [Maybe a] -> [a]
$ hoogle "Monad m => [m a] -> m [a]"
Prelude sequence :: Monad m => [m a] -> m [a]
$ hoogle "[a] -> [b] -> (a -> b -> c) -> [c]"
Prelude zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
所吸引-谷歌程序是不够编写他的方案在纸上通过自己同样的方式,他不借助计算机。但是他和他的机器的一起强迫未*不可忽视的力量。
顺便说一句,如果你喜欢吸引一定要检查出hlint!
免费的定理
菲利浦wadler向我们介绍了这一概念的 免费的定理 我们已经滥用他们在Haskell。
这些奇妙的伪的辛德雷-米诺式的类型系统可帮助出等式推理,通过使用parametricity告诉你关于什么的一个函数 将不会 做。
例如,有两项法律,每个实例的函应满足:
- 从f g。fmap f。fmap g=fmap(f。g)
- fmap id=id
但是,免费的理论告诉我们我们不需要打扰证明第一位的,但鉴于第二它是'自由'的,只是从类型签名!
fmap :: Functor f => (a -> b) -> f a -> f b
你需要一位小心懒惰,但这是部分地涵盖在原始文件,并在Janis Voigtlaender的 更近的纸 关于免费理中存在的 seq
.
简写一个共同名单操作
以下是等效的:
concat $ map f list
concatMap f list
list >>= f
编辑
由于更多的详细资料的请求...
concat :: [[a]] -> [a]
concat
需要的列表,列表,并将它们纳入一个单一的名单。
map :: (a -> b) -> [a] -> [b]
map
地图的功能在一个列表。
concatMap :: (a -> [b]) -> [a] -> [b]
concatMap
相当于 (.) concat . map
:地图的功能在一个列表,并将结果。
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
return :: a -> m a
一个 Monad
有一个 绑定的 操作的,这就是所谓 >>=
在Haskell(或其糖 do
-当量)。列表,aka []
, 是一个 Monad
.如果我们的替代品 []
对于 m
在上述:
instance Monad [] where
(>>=) :: [a] -> (a -> [b]) -> [b]
return :: a -> [a]
什么是自然的事情 Monad
操作要做一个清单?我们必须满足的单的法律,
return a >>= f == f a
ma >>= (\a -> return a) == ma
(ma >>= f) >>= g == ma >>= (\a -> f a >>= g)
你可以验证这些法律,持有,如果我们用执行
instance Monad [] where
(>>=) = concatMap
return = (:[])
return a >>= f == [a] >>= f == concatMap f [a] == f a
ma >>= (\a -> return a) == concatMap (\a -> [a]) ma == ma
(ma >>= f) >>= g == concatMap g (concatMap f ma) == concatMap (concatMap g . f) ma == ma >>= (\a -> f a >>= g)
这是在事实上的行为 Monad []
.作为示范,
double x = [x,x]
main = do
print $ map double [1,2,3]
-- [[1,1],[2,2],[3,3]]
print . concat $ map double [1,2,3]
-- [1,1,2,2,3,3]
print $ concatMap double [1,2,3]
-- [1,1,2,2,3,3]
print $ [1,2,3] >>= double
-- [1,1,2,2,3,3]
嵌套多行评论.
{- inside a comment,
{- inside another comment, -}
still commented! -}
广义的代数数据类型。这里有一个例子,解释程序的类型系统可以让你盖所有情况:
{-# LANGUAGE GADTs #-}
module Exp
where
data Exp a where
Num :: (Num a) => a -> Exp a
Bool :: Bool -> Exp Bool
Plus :: (Num a) => Exp a -> Exp a -> Exp a
If :: Exp Bool -> Exp a -> Exp a -> Exp a
Lt :: (Num a, Ord a) => Exp a -> Exp a -> Exp Bool
Lam :: (a -> Exp b) -> Exp (a -> b) -- higher order abstract syntax
App :: Exp (a -> b) -> Exp a -> Exp b
-- deriving (Show) -- failse
eval :: Exp a -> a
eval (Num n) = n
eval (Bool b) = b
eval (Plus e1 e2) = eval e1 + eval e2
eval (If p t f) = eval $ if eval p then t else f
eval (Lt e1 e2) = eval e1 < eval e2
eval (Lam body) = \x -> eval $ body x
eval (App f a) = eval f $ eval a
instance Eq a => Eq (Exp a) where
e1 == e2 = eval e1 == eval e2
instance Show (Exp a) where
show e = "<exp>" -- very weak show instance
instance (Num a) => Num (Exp a) where
fromInteger = Num
(+) = Plus
模式的顶级绑定的
five :: Int
Just five = Just 5
a, b, c :: Char
[a,b,c] = "abc"
如何很酷是的!节省你说打电话 fromJust
和 head
然后每一个现在.
可选的布局
你可以使用明确的牙套和分号替代的空白(阿卡布)划定界限的区块。
let {
x = 40;
y = 2
} in
x + y
...或者...
let { x = 40; y = 2 } in x + y
...而不是...
let x = 40
y = 2
in x + y
因为布局不是必需的,Haskell的程序可以是直截了当地生产通过其他程序。
seq
和 ($!)
仅仅评估 足够的检查,事情不是底部。
以下程序只会打印"有"。
main = print "hi " `seq` print "there"
对于那些不熟悉Haskell,Haskell是非严格在一般情况下,这意味着一个参数的一个函数是只评价,如果它是必要的。
例如,下面的印刷品"忽视"和终止与成功。
main = foo (error "explode!")
where foo _ = print "ignored"
seq
是已知的变化,行为,通过评估到底,如果它的第一个论点是底部。
例如:
main = error "first" `seq` print "impossible to print"
...或者,不缀...
main = seq (error "first") (print "impossible to print")
...会毁了一个错误的"第一"。它将永远不会打印"不可能打印"。
所以这可能是一个有点令人惊讶的是,尽管 seq
是严格的,不会评估东西方渴望的语言进行评估。特别是,它不会试图迫使所有正整数在以下程序。相反,它将检查 [1..]
不是底部(它可以立即找到),印刷"完成",以及退出。
main = [1..] `seq` print "done"
操作的固定性
你可以使用 中缀infixl或infixr 关键词定义的运营者结合性和优先顺序。例取自的 参考:
main = print (1 +++ 2 *** 3)
infixr 6 +++
infixr 7 ***,///
(+++) :: Int -> Int -> Int
a +++ b = a + 2*b
(***) :: Int -> Int -> Int
a *** b = a - 4*b
(///) :: Int -> Int -> Int
a /// b = 2*a - 3*b
Output: -19
数(0到9)后缀允许你定义的优先次序的操作者,9最强。缀意味着没有关联性,而infixl associates左infixr伙伴的权利。
这可以让你定义的复杂运营商的做高级别的操作书面作为简单的表达方式。
注意,你也可以使用的二进制的职能运营,如果你把他们之间的反引号:
main = print (a `foo` b)
foo :: Int -> Int -> Int
foo a b = a + b
正因为如此,也可以定义优先于他们:
infixr 4 `foo`
避免括号内的
的 (.)
和 ($)
职能 Prelude
有很方便的fixities,让你避免括号内的在许多地方。以下是等效的:
f (g (h x))
f $ g $ h x
f . g $ h x
f . g . h $ x
flip
也有帮助,下面是等效的:
map (\a -> {- some long expression -}) list
flip map list $ \a ->
{- some long expression -}
漂亮的警卫
Prelude
定义 otherwise = True
, ,使得完全的保护条件非常自然的。
fac n
| n < 1 = 1
| otherwise = n * fac (n-1)
C式枚举
结合顶级模式匹配和算术列为我们提供了一个方便的方式来定义连续值:
foo : bar : baz : _ = [100 ..] -- foo = 100, bar = 101, baz = 102
可读功能组成
Prelude
定义 (.)
是数学的功能组成;就是 g . f
第一适用 f
, 然后适用 g
结果。
如果你 import Control.Arrow
, 以下是当量:
g . f
f >>> g
Control.Arrow
提供了一个 instance Arrow (->)
, ,这是很好的人不喜欢阅读功能的应用倒退。
let 5 = 6 in ...
是有效的Haskell.
无限的清单
由于你所提到的斐波那契数,有一种非常优雅的方式 产生斐波那契数字 从一个无限的清单是这样的:
fib@(1:tfib) = 1 : 1 : [ a+b | (a,b) <- zip fib tfib ]
该@操作者允许使用模式匹配的1:tfib结构的同时,仍然参考的整个模式作为fib。
注意到理解列表进入一个无限递归的,生成一个无限的清单。然而,可以请求因素,从它或它们进行操作,只要你请求一种有限的额:
take 10 fib
你也可以适用一个操作的所有要素之前,要求它们:
take 10 (map (\x -> x+1) fib)
这是由于Haskell的懒惰的评价参数和列表。
灵活规范的模块进口和出口
进口和出口是好的。
module Foo (module Bar, blah) -- this is module Foo, export everything that Bar expored, plus blah
import qualified Some.Long.Name as Short
import Some.Long.Name (name) -- can import multiple times, with different options
import Baz hiding (blah) -- import everything from Baz, except something named 'blah'
如果你是在寻找一个清单或更高阶功能,它已经存在
有sooo多的方便和高功能的标准图书馆。
-- factorial can be written, using the strict HOF foldl':
fac n = Data.List.foldl' (*) 1 [1..n]
-- there's a shortcut for that:
fac n = product [1..n]
-- and it can even be written pointfree:
fac = product . enumFromTo 1
等式推理
Haskell,正在纯粹的功能让你读一个等号,作为一个真正平等的符号(在没有非重叠的模式)。
这可以让你取代定义,直接进入代码,并在优化提供了很大的回旋余地来编译器有关当事情发生.
一个很好的例子,这种形式的推理可以在这里找到:
http://www.haskell.org/pipermail/haskell-cafe/2009-March/058603.html
这也表现很好的形式的法律或规则说明预期的有效成员的一个实例,例如单法律:
- returrn一个>>=f==f
- m>>=返回==m
- (m>>=f)>>=g==m>>=(\x->f x>>=g)
往往可以用于简化一元代码。
懒惰
普遍存在的懒惰意味着你可以做的事情一样定义
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
但它也为我们提供了一个很大的更微妙的利益而言的语法和推理。
例如,由于严格ML已处理的 值的限制, 和非常仔细跟踪圆让我们绑定,但在Haskell,我们可以让每一个让被递归和没有必要区分 val
和 fun
.这样可以消除的一个主要的语法疣从语言。
这间接地提升我们的可爱的 where
条款,因为我们可以安全地移动计算,可以或不可以使用的主要的控制流动和让懒惰处理共享的结果。
我们可以更换(几乎)所有这些毫升风格的功能需要采取()和返回值,只有一个懒惰的计算值。有理由避免这样做时要避免泄漏的空间 水产科学, 但这种情况是罕见的。
最后,它允许不受限制的eta-减少(\x -> f x
可以换成f)。这使得组合为导向的方案编制的东西喜欢分析器组合子更舒适于工作的具有类似结构的严格语言。
这可以帮助你当推理有关的程序中点的自由风格的,或关于改写他们的成点的自由风格,并降低参数的噪音。
平行名单的理解
(特别GHC特征)
fibs = 0 : 1 : [ a + b | a <- fibs | b <- tail fibs ]
枚举
任何类型的实例 Enum 可以使用在算术的顺序,不仅仅是数字:
alphabet :: String
alphabet = ['A' .. 'Z']
包括你自己的数据类型,就得从枚举,得到默认的执行情况:
data MyEnum = A | B | C deriving(Eq, Show, Enum)
main = do
print $ [A ..] -- prints "[A,B,C]"
print $ map fromEnum [A ..] -- prints "[0,1,2]"
单子
他们不是隐藏的,但是他们简直是无处不在,甚至在那里你不要认为他们(单,也许-类型)...