我已经读过《学Haskell的学习》书,直到他们介绍Monad和诸如Just a之类的东西为止。这些对我来说太违反直觉了,以至于我只是想放弃尝试学习它。
但是我真的很想尝试。
我想知道我是否至少可以暂时避开高级概念,而只是开始使用语言的其他部分来完成Haskell的更基本部分的许多实际任务,即函数,标准IO,文件处理,数据库接口等。
这是学习使用Haskell的合理方法吗?
我已经读过《学Haskell的学习》书,直到他们介绍Monad和诸如Just a之类的东西为止。这些对我来说太违反直觉了,以至于我只是想放弃尝试学习它。
但是我真的很想尝试。
我想知道我是否至少可以暂时避开高级概念,而只是开始使用语言的其他部分来完成Haskell的更基本部分的许多实际任务,即函数,标准IO,文件处理,数据库接口等。
这是学习使用Haskell的合理方法吗?
Answers:
首先,让我们区分学习抽象概念和学习抽象概念的具体实例。
您不会无视所有具体示例,原因很简单,它们无处不在。实际上,抽象之所以存在,很大程度上是因为它们通过特定的示例统一了您将要执行的操作。
另一方面,抽象本身当然是有用的,但并不是立即需要的。您完全可以完全忽略抽象,而直接使用各种类型就可以了。您最终想了解它们,但是您以后总是可以重新使用它。实际上,我几乎可以保证,如果您这样做了,那么当您重温一下额头时,就会想知道为什么您总是花这么长时间来辛苦地做事,而不是使用便捷的通用工具。
以Maybe a
作为一个例子。这只是一种数据类型:
data Maybe a = Just a | Nothing
几乎都是自我记录;这是一个可选值。您只是“拥有”某种类型的东西a
,或者您什么也没有。假设您具有某种查找功能,该查找功能返回Maybe String
以表示查找String
可能不存在的值。因此,您可以对值进行模式匹配以查看它是什么:
case lookupFunc key of
Just val -> ...
Nothing -> ...
就这样!
真的,您不需要其他任何东西。没有Functor
s或Monad
s或其他任何东西。这些表达了使用Maybe a
值的通用方法……但是它们只是惯用语,“设计模式”,无论您要调用什么。
一个您真正无法避免的地方就是IO
,但是无论如何这都是一个神秘的黑匣子,因此不值得尝试理解它的含义Monad
。
实际上,这是您现在真正需要了解的所有备忘单IO
:
如果某物具有type IO a
,则意味着该过程执行某物并吐出一个a
值。
当您使用该do
符号的代码块时,编写如下内容:
do -- ...
inp <- getLine
-- etc...
...意味着执行右侧的过程<-
,并将结果分配给左侧的名称。
而如果您有这样的事情:
do -- ...
let x = [foo, bar]
-- etc...
...这意味着将普通表达式(而不是过程)的值分配给=
左侧的名称的右侧。
如果您在此处放置某些东西而未分配值,则如下所示:
do putStrLn "blah blah, fishcakes"
...这意味着执行一个过程并忽略它返回的任何内容。某些过程具有类型IO ()
-该()
类型是一个占位符,它什么也没说,因此就意味着该过程执行了某些操作并且不返回任何值。有点像void
其他语言中函数。
多次执行相同的步骤会产生不同的结果;那是个主意。这就是为什么无法IO
从值“删除”的原因,因为in中的IO
内容不是值,而是获取值的过程。
do
块中的最后一行必须是无分配的普通过程,该过程的返回值将成为整个块的返回值。如果您希望返回值使用已经分配的某个值,则该return
函数将采用一个纯值,并提供一个无操作过程来返回该值。
除此之外,没有什么特别的IO
。这些过程本身实际上就是简单的值,您可以传递它们并以不同的方式组合它们。只有当它们在do
某个地方被调用时main
,它们才能执行任何操作。
因此,在像这样完全无聊的陈规定型示例程序中:
hello = do putStrLn "What's your name?"
name <- getLine
let msg = "Hi, " ++ name ++ "!"
putStrLn msg
return name
...您可以像命令式程序一样阅读它。我们正在定义一个名为的过程hello
。执行后,它首先执行一个过程以打印询问您姓名的消息;接下来,它执行一个过程,该过程读取一行输入,并将结果分配给name
;然后为其分配一个表达式msg
; 然后打印消息;然后它返回用户名作为整个块的结果。既然name
是a String
,这意味着hello
返回a的过程String
,所以它具有类型IO String
。现在,您可以在其他地方执行此过程,就像执行一样getLine
。
PFFF,单子。谁需要他们?
Haskell的高级概念(如Monads和Applicative Functor)对大多数常规编程任务有多重要?
它们是必不可少的。没有它们,将Haskell用作另一种命令性语言太容易了,尽管Simon Peyton Jones曾经称Haskell为“世界上最好的命令性编程语言”,但您仍然会错过最好的部分。
Monad,Monad变换,Monoids,函子,应用函子,逆变函子,箭头,类别等是设计模式。一旦将特定的东西放入其中之一,您就可以利用大量抽象编写的功能。这些类型的类不仅仅在那里使您感到困惑,它们还在那里使您的生活更轻松并提高您的生产力。在我看来,它们是简洁的Haskell代码简洁的最大原因。
如果您只想运行某些程序,是否绝对必要?没有。
如果您想编写漂亮的惯用Haskell程序,是否有必要?绝对。
在Haskell中进行编程时,请记住两点:
类型系统可以为您提供帮助。如果您是使用高度多态的typeclass繁重的代码来编写程序的,那么通常您会遇到一种奇妙的情况,所需的函数类型将指导您(一个!)正确的实现。
使用原则1开发了各种可用的库。
因此,在用Haskell编写程序时,我首先编写类型。然后,我重新开始并写下一些更通用的类型(如果它们可以是现有类型类的实例,那就更好了)。然后,我编写了一些函数,这些函数为我提供了所需类型的值。(然后,我和他们一起玩,ghci
也许写了一些quickCheck测试。)最后,我将它们粘合在一起main
。
可能编写丑陋的Haskell只是使某些事情运行。但是,一旦达到该阶段,还有很多东西需要学习。
祝好运!