Haskell的高级概念(如Monads和Applicative Functor)对大多数常规编程任务有多重要?


16

我已经读过《学Haskell的学习》书,直到他们介绍Monad和诸如Just a之类的东西为止。这些对我来说太违反直觉了,以至于我只是想放弃尝试学习它。

但是我真的很想尝试。

我想知道我是否至少可以暂时避开高级概念,而只是开始使用语言的其他部分来完成Haskell的更基本部分的许多实际任务,即函数,标准IO,文件处理,数据库接口等。

这是学习使用Haskell的合理方法吗?


2
我不知道你能走多远。如果没有monad,您将无法在Haskell中做任何有用的事情,因为要在编程中做任何有用的事情(例如,编写某人实际上想使用的严肃程序)都需要I / O和状态,这两者只能在Haskell中进行管理通过使用单子。
梅森惠勒

1
@dan-通过阅读两个文档,我终于理解了Haskell monads:“您可能已经发明了monads”和“解决笨拙的团队”。我不能说它们是否比“我还没读过”“学Haskell”更好,但是它们为我工作。要对IO monad使用do表示法,您几乎不需要了解monad是什么-您将做一些会使专家大笑或哭泣的事情,但至少从表面上看,与使用monad并没有什么不同传统的命令式语言。但它值得的得到一些更深入的了解。
Steve314 2011年

2
@dan,我花了将近十年的时间才对OOP感到无聊,并开始欣赏/好奇函数式语言。在认真尝试Haskell之前,我仍然会学习F#和Clojure。虽然个人挑战很好,但对于您的直觉和抵抗力最小的途径,还有一些要说的。在很大程度上取决于你是谁。如果您的数学能力很强,那么您可能从一开始就喜欢Haskell / Schem。如果您更像一个“完成任务”的人,那也可以。不要以“做死”的态度接近Haskell。继续学习其他东西,无聊的时候回来。
Job

感谢@Job的建议。我已经尝试过Clojure了一段时间,但是Lisp语法确实对我不起作用,无论是读取还是键入。我发现Haskell的语法更自然。同时,我很高兴将Ruby用于项目,但我希望开始在Haskell中进行一些实际的项目。

@ dan,Funny,我发现Clojure的语法相当友好,但这可能是因为我以前接触过Scheme。也许在F#之后,我会习惯于Haskell做事的方式。
工作

Answers:


16

首先,让我们区分学习抽象概念和学习抽象概念的具体实例

您不会无视所有具体示例,原因很简单,它们无处不在。实际上,抽象之所以存在,很大程度上是因为它们通过特定的示例统一了您将要执行的操作。

另一方面,抽象本身当然是有用的,但并不是立即需要的。您完全可以完全忽略抽象,而直接使用各种类型就可以了。您最终想了解它们,但是您以后总是可以重新使用它。实际上,我几乎可以保证,如果您这样做了,那么当您重温一下额头时,就会想知道为什么您总是花这么长时间来辛苦地做事,而不是使用便捷的通用工具。

Maybe a作为一个例子。这只是一种数据类型:

data Maybe a = Just a | Nothing

几乎都是自我记录;这是一个可选值。您只是“拥有”某种类型的东西a,或者您什么也没有。假设您具有某种查找功能,该查找功能返回Maybe String以表示查找String可能不存在的值。因此,您可以对值进行模式匹配以查看它是什么:

case lookupFunc key of
    Just val -> ...
    Nothing  -> ...

就这样!

真的,您不需要其他任何东西。没有Functors或Monads或其他任何东西。这些表达了使用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,单子。谁需要他们?


2
@dan:非常欢迎!如果您在此过程中有任何具体问题,请随时询问Stack Overflow。[haskell]标签通常非常活跃,如果您解释自己的来历,人们会很好地帮助您获得理解,而不仅仅是抛出含糊的答案。
CA McCann

9

Haskell的高级概念(如Monads和Applicative Functor)对大多数常规编程任务有多重要?

它们是必不可少的。没有它们,将Haskell用作另一种命令性语言太容易了,尽管Simon Peyton Jones曾经称Haskell为“世界上最好的命令性编程语言”,但您仍然会错过最好的部分。

Monad,Monad变换,Monoids,函子,应用函子,逆变函子,箭头,类别等是设计模式。一旦将特定的东西放入其中之一,您就可以利用大量抽象编写的功能。这些类型的类不仅仅在那里使您感到困惑,它们还在那里使您的生活更轻松并提高您的生产力。在我看来,它们是简洁的Haskell代码简洁的最大原因。


1
+1:感谢您指出“ Monad,Monad变换,Monoids,函子,应用函子,相反函子,箭头,类别等是设计模式。” !
乔治

7

如果您只想运行某些程序,是否绝对必要?没有。

如果您想编写漂亮的惯用Haskell程序,是否有必要?绝对。

在Haskell中进行编程时,请记住两点:

  1. 类型系统可以为您提供帮助。如果您是使用高度多态的typeclass繁重的代码来编写程序的,那么通常您会遇到一种奇妙的情况,所需的函数类型将指导您(一个!)正确的实现

  2. 使用原则1开发了各种可用的库。

因此,在用Haskell编写程序时,我首先编写类型。然后,我重新开始并写下一些更通用的类型(如果它们可以是现有类型类的实例,那就更好了)。然后,我编写了一些函数,这些函数为我提供了所需类型的值。(然后,我和他们一起玩,ghci也许写了一些quickCheck测试。)最后,我将它们粘合在一起main

可能编写丑陋的Haskell只是使某些事情运行。但是,一旦达到该阶段,还有很多东西需要学习。

祝好运!


1
谢谢!我一直在Clojure和Haskell之间摇摆不定,但是现在我倾向于Haskell,因为像您这样的人有多么乐于助人。

1
Haskell社区似乎非常友好和乐于助人。这似乎是很多有趣的想法的来源
Zachary K
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.