Giter Club home page Giter Club logo

haskellcn's Introduction

haskellcn's People

Contributors

freizl avatar haishengwu-okta avatar hw202207 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

haskellcn's Issues

[转]函数式编程入门讲稿 by byvoid

以下内容是我在某信息学竞赛(APIO2012)上授课内容的讲稿——函数式编程。主要介绍了lambda演算、Y组合子、Haskell入门、函数式编程的简单算法设计**以及工程中的函数式编程。面向的对象是完全没有听说过函数式编程,但对算法设计有较深了解的学生。

1
2
3
4
5

全部内容见 http://www.byvoid.com/blog/apio-fp/

网站不维护了吗?

  1. 发现网站好像用户量还是比较少。
  2. 好久没有维护了?还是有新的**社区站点? 👍

关于monomorphism restrictoin的问题 by xiaoxiaflash

在看Haskell report其中关于monomorphism restriction产生的一些现象不是很理解。

我觉得monomorphism restriction是由于shared evaluation类型可能为多态而加入的。
比如(来自 haskell wiki)

import Data.List (genericLength)
f xs = let len = genericLength xs in (len, len) 

在 monomorphism restriction下,类型为Num t => [b] -> (t, t)

但是如果禁止monomorphism restriction
类型会为为 (Num t, Num t1) => [b] -> (t, t1)。这样就会计算长度2次。

但是,monomorphism restriction是如何影响eta化简的呢?又是怎么引起pattern match与lambda表达式有区别呢?

f x = show x      -- (合法) 
f = show         -- (不合法)

或者函数 g = (==)          -- (不合法) 
g x y = x == y               -- (合法) 
g = \x -> \y -> (x==y)     -- (不合法)

学 Haskell 应该走哪几个过程? by jiyinyiyong

测试帖.. 发现预览功能和标签和 CNode 上对比是不是 bug
Uncaught ReferenceError: $ is not defined Chrome 上报错

搭上自我介绍.. 小弟大三, 学不好数学和 C, 于是在网上翻资料想学玩代码. Linux 党.
入门的时候绕几种语言, 后来为了语法去熟悉了 Haskell, 没能深入..
接触函数式编程后 CoffeeScript 就感觉顺多了, 就很浅地开始玩 Node

特别喜欢的还是 Haskell, 但一直都有种太高无法接近的感觉
所以特别想了解开始学 Haskell, 基础语法之后, 怎样开始深入学和写实用的代码?
还有比方入门的时候应该从哪些代码进行练习熟悉和掌握?
然后这后面又应该从什么方面继续接触 Haskell 深入的东西?

手把手教你做λ(四)语法识别器、高阶函数和列表推导 by ninegua

上一节里我们实现了把 Term 类型转换为字串的 show 函数,解决了输出问题。接下来,我们要反过来,考虑该如何把一个代表λ项的字串转换为 Term 类型的数据结构,这也叫做语法识别器 (parser)。在 Haskell 的标准库里,有一个跟 Show 对应的类型类 (type class),叫做 Read

  type ReadS a = String -> [(a, String)]
  class Read a where
    readsPrec :: Int -> ReadS a

类型 ReadS a 所代表的就是对类型 a 的识别器,它是这样一个函数:对给定的字串参数从左到右进行识别 (parse),如果成功,则返回所识别的值 (属于 a 类型) 和剩下的字串;如果不成功,则返回空列表。通常成功时只返回一个值,但是考虑到有时候需要允许歧义,用列表也可以返回多个值,以代表所有的可能性。

而定义类型类 Read 的实例 (instance) 则需要定义一个 readsPrec 函数,它的参数代表优先级,返回的则是 ReadS a 所代表的识别器。我们这里用不到优先级,可以忽略掉。主要任务是实现类型 Var 和类型 Term 的识别器。

假设λ表达式里所有的变元都是单个字母,那么很容易就可以写出如下的识别器:

  import Data.Char

  variable :: ReadS Var
  variable (c:s) | isAlpha c = [(V [c], s)]
  variable _                 = []

  instance Read Var where
    readsPrec _ = variable   

这里我们用到了模式匹配来判断字串的第一个字符是否是字母(isAlpha 函数出自 Data.Char 模块),如果是,则成功返回符合 Var 类型的值 V [c] 和余下的字串 s。否则返回空列表 [],代表识别失败。其中 | isAlpha c = ... 的写法叫做 guard,意思是满足条件 isAlpha c == True 的情况下才计算右边。这是一种语法糖 (syntactic sugar),意即非关键的语法性能,它可以完全转成 case 的写法。

实际上,我们会经常用到单个字符的识别器,所以这个功能可以单独被提取出来,成为下面的函数:

  char :: (Char -> Bool) -> ReadS Char
  char f (c:s) | f c = [(c, s)]
  char f _           = []

这样一来,用 char isAlpha 就可以识别所有的字母。细心的读者会发现,char isAlphaReadS Char 类型,那怎么把它变成我们需要的 ReadS Var 类型呢?根据 ReadS 的定义,其返回的值是一个列表,那么事情就简单了:

  variable s = map f (char isAlpha s)
    where f (c, s1) = (V [c], s1)

注意到 char isAlpha s 返回的是 [(Char,String)] 类型,我们要把它变成 variable s 所需要返回的 [(Var, String)] 类型,而这可以用 map :: (a -> b) -> [a] -> [b] 来做到。如果 f 是从类型 a 到类型 b 的映射,那么 map f 则是从类型 [a] 到类型 [b]。换句话说,map 是一个高阶函数 (higher-order function),它能够把一个 a -> b 类型的函数,转换 [a] -> [b] 类型的函数,用于处理列表。

关于列表的运算,除了使用 map 这样的函数,在 Haskell 里还可以用推导式 (comprehension) 来表示,比如 variable 还可以写成:

  variable s = [(V [c], s1) | (v, s1) <- char isAlpha s]

列表推导式也是一种语法糖,上述两种 variable 函数的写法完全等价,而使用推导式有时能够帮助理解复杂的表达式。这很类似于数学里关于集合运算的写法,比如上面这个定义就可以理解为:对于列表 char isAlpha s 里的每一个元素 (v, s1),生成新的元素 (V [c], s1) 并组成新的列表返回。

假设我们规定变元必须是两个字母,那么该如何实现这样的识别器?这就需要用到复杂一点的列表推导式:

  variable2 s = [ (V [c1,c2], s2) 
                | (c1, s1) <- char isAlpha s 
                , (c2, s2) <- char isAlpha s1 ]

这可以简单理解为,在 s 字串中识别一个字母 c1,在剩下的 s1 字串中识别第二个字母 c2,将两个字母拼成一个字串 [c1,c2] 成为识别结果。

习题

一、在不使用 GHC 或 GHCi 的情况下,(心算)给出下列表达式的值:

  1. variable ""
  2. variable "a"
  3. variable "123"
  4. variable "abc"
  5. variable2 ""
  6. variable2 "a"
  7. variable2 "abc"

二、把 variable2 用普通方法定义,不要使用列表推导。如果要更挑战一点,可以考虑仅用 mapconcat

三、定义一个函数 variableN :: Int -> ReadS Var,根据参数 n 生成长度为 n 的变元识别器。仅考虑 n >= 0 的情况。

学 Haskell 应该走哪几个过程? by jiyinyiyong

测试帖.. 发现预览功能和标签和 CNode 上对比是不是 bug
Uncaught ReferenceError: $ is not defined Chrome 上报错

搭上自我介绍.. 小弟大三, 学不好数学和 C, 于是在网上翻资料想学玩代码. Linux 党.
入门的时候绕几种语言, 后来为了语法去熟悉了 Haskell, 没能深入..
接触函数式编程后 CoffeeScript 就感觉顺多了, 就很浅地开始玩 Node

特别喜欢的还是 Haskell, 但一直都有种太高无法接近的感觉
所以特别想了解开始学 Haskell, 基础语法之后, 怎样开始深入学和写实用的代码?
还有比方入门的时候应该从哪些代码进行练习熟悉和掌握?
然后这后面又应该从什么方面继续接触 Haskell 深入的东西?

英文术语翻译成中文有哪些参照啊? by jiyinyiyong

试着翻译下博文遇到这个问题了, 先就网上翻单词, 然后有不靠谱的..
照例 Google, 先就 read-the-docs 上找到一些, 50 组左右吧,
http://haskell_tutorial.readthedocs.org/en/latest/keywords.html
豆瓣搜到的有 10+ 个,
http://www.douban.com/group/topic/22123880/
然后 learn-you 前 8 章有翻译, 可不会这再详细看一遍了, 刚好英文看到第 8 章..
http://fleurer-lee.com/lyah/chapters.htm
再多点术语要怎么照着看啊?

在 OpenShift 上使用 Warp by JackRose

参考A PaaS that runs anything HTTP: Getting Started with DIY Applications on OpenShift
使用getEnv获得环境变量OPENSHIFT_INTERNAL_IP以及OPENSHIFT_INTERNAL_PORT

helloworld.hs

{-# LANGUAGE OverloadedStrings #-}

module Main 
	(
		main
	) 
where

import Network.Wai.Handler.Warp
import System.Environment
import Network.Wai
import Network.HTTP.Types
import Control.Monad.Trans.Resource
import Data.Conduit.Network


getOpenShiftServerSettings :: IO Settings
getOpenShiftServerSettings = do
	internalIP <- getEnv "OPENSHIFT_INTERNAL_IP"
	internalPORT <- getEnv "OPENSHIFT_INTERNAL_PORT"
	return $ defaultSettings {settingsHost = Host internalIP , settingsPort = read internalPORT}


sayHelloWorld::Request -> ResourceT IO Response
sayHelloWorld  req = return $ responseLBS status200 [("Content-Type", "text/plain")] "Hello World" 


main::IO ()
main = do
	set <- getOpenShiftServerSettings
	runSettings set sayHelloWorld

静态编译(X86_64)

ghc --make -static -optl-static -optl-pthread helloworld.hs

修改参考中提到的 start 以及 stop 脚本
将静态编译所得程序push到OpenShift的repo中。

静态编译之警告

glibc中的iconv 的原因程序未能完全静态链接,程序对glibc依赖。 ghc7的string 依赖iconv

hackage.haskell.org 不能访问了么? by julian

昏啊,因为升级了mac os,所以之前安装的软件都不能用了,xcode也是刚刚重新安装。

由于haskell也不能用了,所以重现安装。本来是打算直接状态hp 2012的,但是再我的机器上hackage.haskell.org总是不能访问。

所以想问一下大家可以访问么?是出了什么情况啊

今天看到的关于尾递归和续延的东西 by Liutos

今天看了老赵的一篇博客,链接为http://blog.zhaojie.me/2009/03/tail-recursion-and-continuation.html,说到了尾递归和续延的关系。我看着看着也想到了,尾递归实际上就是把本来应该在当前函数调用中完成的事情,以某种形式保留下来传递到一下次的函数调用中去,然后在遇到递归的base case的时候再全部计算出来吧。

这里的某种形式,我目前看到的,一是多设置一个形参作为``累加器'',然后把当前的值传递给续延并将计算结果传给这个参数进行一下次调用。例如下面这个形式的factorialNormal函数的定义

factorialNormal n =
  let rec acc n =
        if n == 0
        then acc
        else rec (acc * n) (n - 1)
  in rec 1 n

还有一个今天刚看到的,也刚刚弄懂的,是使用续延的版本,这里用构造匿名函数来代替续延好了。函数如下

factorialContinuation n cont =
  if 0 == n
  then cont 1
  else factorialContinuation (n - 1) (\r -> cont (n*r))

factorialRecursive n =
  factorialContinuation (n-1) (\r -> r*n)

我本来以为尾递归其实是一种技巧,单纯是使用累加器之类的来实现的。没想到原来尾递归和续延似乎在背后还是有一些关系的。这些东西有没有专门的研究的呢?

关于使用par对同一个变量进行计算的问题 by jhiter

假设对x的计算是一个很费时的操作,那么有谁能解释一下当执行x par x时发生了什么吗?多谢!
比如:

f :: Int -> Int
f 0 = 1
f 1 = 1
f n = x `par` x
        where x = (f (n-1)) + (f (n - 2))

知道这个例子很怪,只是想知道这样做时发生了什么?

haskell的import有点奇葩..默认import有点"空间合并"的味道.. by youseeli

如题..可能标题表达的不清楚..

我的意思是..

如MyModule里有myfunc.那么import MyModule然后直接就可以引用myfunc

这样写出来的代码可读性太差了..如果import 几个module 然后代码里的func就很难知道是哪个module里的了..

我觉得应该默认就是import qualified MyMoudle的效果..而现在improt的效果应该用其它关键字代替.

转:Haskell Is Exceptionally Unsafe by puffy

http://existentialtype.wordpress.com/2012/08/14/haskell-is-exceptionally-unsafe/

It is well known that Haskell is not type safe. The most blatant violation is the all too necessary, but aptly named, unsafePerformIO operation. You are enjoined not to use this in an unsafe manner, and must be careful to ensure that the encapsulated computation may be executed at any time because of the inherent unpredictability of lazy evaluation. (As an aside, it is worth mentioning that the analogous operation in monadic ML, safePerformIO, is entirely safe, precisely because of the value restriction on polymorphism.) A less blatant violation is that the equational theory of the language with seq is different from the equational theory without it, and the last I knew the compiler was willing to make transformations that are valid only in the absence of this construct. This too is well-known. A proper reformulation of the equational theory was given by Patricia Johann a few years ago as a step towards solving it. (If the GHC compiler is no longer unsafe in this respect, it would be good to know.)

I’ve asked around a little bit, including some of the Haskell insiders, whether it is equally well known that the typed exception mechanism in Haskell is unsound. From what I can tell this seems not to be well-understood, so let me explain the situation as I see it, and I am sure I will be quickly corrected if I am wrong. The starting point for me was the realization that in Haskell pure code (outside of the IO monad) may raise an exception, and, importantly for my point, the exceptions are user-defined. Based on general semantic considerations, it seemed to me that this cannot possibly be sound, and Karl Crary helped me to isolate the source of the problem.

The difficulty is really nothing to do with exceptions per se, but rather with exception values. (So my point has nothing whatsoever to do with the concept of imprecise exceptions in Haskell.) It seems to me that the root cause is an all-too-common misunderstanding of the concept of typed exceptions as they occur, for example, in Standard ML. The mistake is a familiar one, the confusion of types with classes; it arises often in discussions related to oop. To clarify the situation let me begin with a few remarks exception values in ML, and then move on to the issue at hand.

The first point, which is not particularly relevant to the present discussion, is that exceptions have nothing to do with dynamic binding. The full details are in my book, so I will only summarize the situation here. Many seem to have the idea that an exception handler, which typically looks like a sequence of clauses consisting of an exception and an action, amounts to a dynamic binding of each exception to its associated handler. This is not so. In fact the left-hand side of an exception clause is a pattern, including a variable (of which a wild card is one example), or nested patterns of exceptions and values. I realize that one may implement the exception mechanism using a single dynamically bound symbol holding the current exception handler, but this implementation is but one of many possible ones, and does not in any case define the abstraction of exceptions. Exceptions have no more to do with dynamic binding than does the familiar if-then-else available in any language.

The second point, which is pertinent to this discussion, is that exceptions have only one type. You read that right: the typed exception mechanism in Standard ML is, in fact, uni-typed (where I have I heard that before?). It is perfectly true that in Standard ML one may associate a value of any type you like with an exception, and this value may be communicated to the handler of that exception. But it is perfectly false to say that there are multiple types of exceptions; there is only one, but it has many (in fact, “dynamically many”) classes. When you declare, say, exception Error of string in Standard ML you are introducing a new class of type string->exn, so that Error s is an exception value of type exn carrying a string. An exception handler associates to a computation of type α a function of type exn->α that handles any exceptions thrown by that computation. To propagate uncaught exceptions, the handler is implicitly post-composed with the handler fn x => raise x. Pattern matching recovers the type of the associated value in the branch that handles a particular exception. So, for example, a handler of the form Error x => exp propagates the fact that x has type string into the expression exp, and similarly for any other exception carrying any other type. (Incidentally, another perfectly good handler is Error “abcdef” => exp, which handles only an Error exception with associated value “abcdef”, and no other. This debunks the dynamic binding interpretation mentioned earlier.)

The third point is that exception classes are dynamically generated in the sense that each evaluation of an exception declaration generates a fresh exception. This is absolutely essential for modularity, and is extremely useful for a wide variety of other purposes, including managing information flow security within a program. (See Chapter 34 of my book for a fuller discussion.) O’Caml attempted to impose a static form of exceptions, but it is now recognized that this does not work properly in the presence of functors or separate compilation. This means that exception declarations are inherently stateful and cannot be regarded as pure.

This got me to wondering how Haskell could get away with user-defined typed exceptions in “pure” code. The answer seems to be “it can’t”, as the following example illustrates:

import Control.Exception

import Data.Typeable

newtype Foo = Foo (() -> IO ())

{- set Foo’s TypeRep to be the same as ErrorCall’s -}

instance Typeable Foo where

typeOf _ = typeOf (undefined :: ErrorCall)

instance Show Foo where show _ = “”

instance Exception Foo

main = Control.Exception.catch (error “kaboom”) (\ (Foo f) -> f ())

If you run this code in GHC, you get “internal error: stg_ap_v_ret”, which amounts to “going wrong.” The use of exceptions is really incidental, except insofar as it forces the use of the class Typeable, which is exploited here.

How are we to understand this unsoundness? My own diagnosis is that typed exceptions are mis-implemented in Haskell using types, rather than classes. There is no need in ML for any form of type casting to implement exceptions, because there is exactly one type of exception values, albeit one with many classes. Haskell, on the other hand, tries to have exceptions with different types. This immediately involves us in type casting, and hilarity ensues.

The problem appears to be difficult to fix, because to do so would require that exception values be declared within the IO monad, which runs against the design principle (unavoidable, in my opinion) that exceptions are permissible in pure code. (Exceptions are, according to Aleks Nanevski, co-monadic, not monadic.) Alternatively, since Haskell lacks modularity, a possible fix is to permit only static exceptions in essentially the style proposed in O’Caml. This amounts to collecting up the exception declarations on a whole-program basis, and implicitly making a global data declaration that declares all the exception constructors “up front”. Exception handlers would then work by pattern matching, and all would be well. Why this is not done already is a mystery to me; the current strategy looks a lot like a kludge once you see the problem with safety.

Insofar as my interpretation of what is going on here is correct, the example once again calls into question the dogma of the separation of Church from state as it is realized in Haskell. As beguiling as it is, the idea, in its current form, simply does not work. Dave MacQueen recently described it to me as a “tar baby”, from the Brer Rabbit story: the more you get involved with it, the more entangled you become.

<七周七语言>里举的Monad例子对么? by wuhaisheng

<七周七语言> Day3 里引出Monad的时候用了这么个例子:

问题

Let’s say you have a pirate making a treasure map. He’s drunk, so he picks up a known point and a known direction and makes his way to the treasure with a series of staggers and crawls. A stagger moves two steps, and a crawl moves one step.

解法一

treasureMap d= let d1 = stagger d
                        d2 = stagger d1
                        d3 = crawl d2
                      in d3

解法二 (Monad Way)

data Position t = Position t deriving (Show)
stagger (Position d) = Position (d + 2)
crawl (Position d) = Position (d + 1)
rtn x = x
x >>== f = fx

treasureMap pos = pos >>==
                  stagger >>==
                  stagger >>==
                  crawl >>==
                  rtn

作者这里自己实现rtn>>==用来做例子说明,但我觉得这个他这两个方法和标准的有出入

:t return
return :: Monad a => a -> M a
:t (>>=)
(>>=) :: Monad a => M a -> (a -> M b) -> M b

而例子里的rtn>>==

:t rtn
rtn :: t -> t (a.k.a Position a -> Position a)
:t (>>=)
(>>=) ::  t1 -> (t1 -> t) -> t (a.k.a Position a -> ( Position a -> Position b) -> Position b

显然不一样。而作者所提出的这个问题可以用(.)以chain的方式解决

treasureMap  = crawl . stagger . stagger

附一个简单的测试例子

手把手教你做λ - 引言 by ninegua

《手把手教你做λ》是一个 tutorial 系列,面向正在学习 Haskell 编程的程序员。如果你缺乏对 Haskell 的最基本概念,我建议还是从正式全面的教材开始,比如 Learn You a Haskell for Great Good!

换句话说,这个系列不会教你如何使用正确的语法编写 Haskell 程序,而是如何提高 Haskell 编程的水平
。在开始的时候,你可以没有学过这门语言,甚至是编程新手。但是随着我们内容的展开,你应该能够独立
从 Haskell 的书本上学习到相关基础知识,并参与到本教程中来。

这个系列之所以叫做《手把手教你做λ》,是因为我们的目的是最终用 Haskell 实现一个 Lambda 表达式的
计算器,作为 CGI 程序运行在网站上,最后的效果参见 Lambda Viewer。如果你还不知道什么是 Lambda Calculus,那我们正好一起把它也给学了,因为它是所有函数语言的理论基础与核心。

本系列希望涵盖的内容有:algebraic datatype,recursion,higher-order function,polymorphism,type class,data traversal,monad,functor,applicative,monad transformer,generics,以及 Parsec 等常见库的使用,DSL (domain specific language) 的实现等等。

每一课的内容不多,只会涉及一个知识点甚至更少。会介绍一定的背景知识,并包含少量习题。我尽量做到
两到三日一更,持续至少一到两个月。这样大部分人每次用少量时间就可以跟上进度,想多学一点的则可以
同时精读正规教材。

主要是希望大家能够积极参与讨论和动手实践,越多的互动越好。毕竟学习一门新的编程语言不仅仅是学习
它语法规则和库的使用而已,还要能够用新的方式来思考,来抽象,来表达,来抓住问题的核心并解决它,
从而完成一次自我的提升。这是每一个优秀的程序员的基本素养。

一个locale导致的c2hs bug by tangboyun

错误提示类似下面:

gtk2hsC2hs: Error in C header file.
/usr/include/glib-2.0/glib-object.h:1: (column 0) [FATAL] 
>>> Lexical error!
The character '#' does not fit here.

貌似是locale问题导致的。zh_CN.UTF-8就会导致这个问题。改成en_US.UTF-8就能通过。

貌似09年就有人提过这个中文导致的bug。不过现在装gtk,cairo相关包,还是会出错,只能用

LANG=en_US.UTF-8 cabal install

来装。

手把手教你做λ(二)数据类型的结构与解构 by ninegua

如果你熟悉BNF语法,把λ表达式的结构写出来就是这样的:

  <term>  ::=   <var> | "λ" <var> "." <term> | <term> " " <term>
   <var>  ::=   "a" | "b" | ... | "y" | "z" | ...

前面说过,我们的目标是实现一个λ表达式的计算器。那么在 Haskell 里面如何表示一个λ项呢?我们可以定义下面的数据类型(data type):

  data Term = Var Var | Lam Var Term | App Term Term 
  data Var  = V String

其中紧跟在关键字 data 后的 TermVar 是我们定义的类型名字,等式右边 VarLamAppTerm 类型的 constructor (构造子),VVar 类型的构造子。在 Haskell 里,构造子和类型都要用大写字母开头,而且不禁止构造子和类型名称重复,所以需要根据上下文来判断一下。构造子属于 value 层面,类型属于 type 层面,略为注意就不会混淆。例如第一行的 Var Var 里面,前者是构造子,后者是类型;而 Lam Var Term 这里,第一个 Lam 是构造子,后面的 VarTerm 都是类型。

不难发现,在 Haskell 里定义λ项的数据类型和它的 BNF 语法有些类似。不同之处是前者需要对每种λ项的形态提供一个构造子。这样做的目的是严格区分不同的类型,或者同一个类型的不同部分。这在构造类型的值,或者进行 pattern matching (模式匹配)时都是必要的。

有了以上的定义,我们可以在 Haskell 里面表达任意的λ项了。比如:

  • Var (V "x") 代表变元形态的λ项 x
  • Lam (V "f") (Lam (V "x") (App (Var (V "f")) (Var (V "x")))) 代表 λf.(λx.(f x)),或者简写为 λf.λx.f x

这样构建出来的λ项,也叫做 Term 类型的值。我们不妨把类型看作是一个集合 (set),里面包括各种可以用它的构造子们合法构建的值。比如 Integer 类型的值可以是任意整数,构造子为 0|1|2|...;而 String 类型的值可以是任意长度的字符串。

在 Haskell 里用 data 关键字定义的数据类型,也叫做代数数据类型 (Algebraic Data Type, 或简称 ADT),可以很方便地对各种抽象结构进行表述。定义和运用 ADT 也是高级编程语言的一个基本功能。

数据类型就是数据类型好了,为什么叫 algebraic?一方面是因为可以用自定义的类型名字,例如上面的 TermVar,来指代它们所定义的类型,以构建更多的类型。例如 Var 类型就被用来定义 Term,而 Term 的定义里还用到了它自己,构成一个递归结构。

那么代数数据类型的基本结构是什么?从上面的例子,我们至少可以观察到以下两种:

  1. Product (积)。如果 A、B 指代两种不同的类型,那么 A×B 表示两者的积类型,它的值里面,同时要有一个 A 类型的值与一个 B 类型的值。通常所说的 tuple 类型就是这个意思。

  2. Sum (和)。如果 A、B 指代两种不同的类型,那么 A+B 表示两者的和类型,它的值可以是 A 类型的值,也可以是 B 类型的值。

如果我们用逻辑关系做比方,product 可以看作是“与”,sum 可以看作是“或”。

如果抛开构造子,Term 其实就是 Var + Var × Term + Term × Term。换句话说,Term 类型是三种类型的和:它可以是变元、抽象、或应用,三者之一。其中变元等同与 Var 类型,抽象等同于一个 Var 和一个 Term 的积,而应用等同于一个 Term 和另一个 Term 的积。

实际上在 C 语言里我们也有 struct 和 union,对定的就是 product 和 sum。这么重要的基础概念,反而在多数脚本语言里得不到支持 (尤其是sum),实在是遗憾。

除开 product 和 sum,ADT 还有其它几种基本结构:0 (此类型不包含任何值),1 (此类型只有一个值),和 → (函数结构,有时也叫做幂)。这样一来称之为 algebraic data type 总算名副其实了吧?这些理论源于 category theory,限于篇幅不再一一讨论。

###习题

一、根据上述 TermVar 类型的 Haskell 定义,给出以下λ项所对应的 Haskell 表达式:

  1. a b c d e f
  2. (g h) (i j)
  3. λx.f (λy.f y)
  4. (λf.f x y) (λx.λy.x)
  5. f g λx.y z

二、Haskell 常用的列表类型 [a] 用 product 和 sum 的方式该如何定义?

学 Haskell 应该走哪几个过程?

测试帖.. 发现预览功能和标签和 CNode 上对比是不是 bug
Uncaught ReferenceError: $ is not defined Chrome 上报错

搭上自我介绍.. 小弟大三, 学不好数学和 C, 于是在网上翻资料想学玩代码. Linux 党.
入门的时候绕几种语言, 后来为了语法去熟悉了 Haskell, 没能深入..
接触函数式编程后 CoffeeScript 就感觉顺多了, 就很浅地开始玩 Node

特别喜欢的还是 Haskell, 但一直都有种太高无法接近的感觉
所以特别想了解开始学 Haskell, 基础语法之后, 怎样开始深入学和写实用的代码?
还有比方入门的时候应该从哪些代码进行练习熟悉和掌握?
然后这后面又应该从什么方面继续接触 Haskell 深入的东西?

请问,如何不用cabal安装haskell库, 和如何把module文件放在其它目录 by youseelee

如题 ..因为haskell.org/package/ 在国内访问不了..cabal update/install 用不了..

所以, 请问不用cabal, 用下载源码的方法来安装haskell库可以吗??

国内有haskell库的下载站点吗??

还有..自己请写module时..可以写在其它目录下吗..

比如在module Main同目录下, 建module MyModule于文件MyModule.hs中,

然后在moudle Main中import MyModule就行了..

但如果我想把MyModule.hs放在lib目录下, 而ghc或runghc时又能正确读取module MyModule, 那么我应该怎么做??

RSS的标题链接错误 by puffy

在RSS的输出里,标题链接URL是haskell.cn/....而不是a.haskell.cn,从google reader直接跳过来会到错误页面。

lyah 函子章节 部分翻译内容,大家看看 by julian

Functors, Applicative Functors and Monoids

Haskell是同时具备了纯函数性,高阶函数,参数化代数数据类型和类型类的一门语言,它允许我们 比其他语言在实现时拥有更高水平的多态性(抽象描述能力) 。我们不必去设想一个类属于一个大的类型层次。相反,我们分析哪些类型行为相似然后归类到合适的类型类。 一个Int可以有很多不同的行为/用途。它可以用于equatable(判断是否相等),可以用于排序,可以用于枚举,等。

类型类是开放的,这意味着,我们可以定义自己的数据类型,通过它关联的类型类来定义和描述它的行为方式。 再加上Haskell伟大的类型系统,让我们仅从函数的类型声明中就可以了解很多有用的信息。我们可以通过定义类型类来定义是非常通用和抽象的行为。 我们已经见过类型类,当定义操作去看有两件事是否相等或它们应有怎样的排序顺序。 这些都是很抽象,很优雅的行为,但我们对他们不觉得有任何非常特殊的,因为我们在现实生活中也在按照这个原则在进行处理。 我们最近见到的函子,是基本的可以被映射的事情。这是类型类使用的一个范例,有用的同时有着幽雅抽象的例子。 在本章中,我们将走近细看函子,包括更加强,更实用的函子版本称为applicative函子。做为同一系列中的成员,我们同样会看一下monoids。

Functors redux

但是这里依然会有一个快速回顾:函子是可以用于做映射的东西,比如列表(list),也许(Maybe) ,树(tree)等。在Haskell中,他们符合类型类 Functor的行为描述,此类型类要求的方法只有一个,即 fmap ,这个方法的类型定义为 fmap :: (a -> b) -> f a -> f b。它说:“给我一个传入a得到b的函数,和一个(或几个)装着a的盒子,我就给你一个(或几个)装着 b 的盒子。也就是说将函数应用到盒子的内部去了。

说明. 很多时候盒子的这个比喻是为了帮助你对函子是如何工作的建立一个直观的印象。稍后我们还会将这个比喻用于 applicative 函子和monads。 对于函子的初学者这是一个可行的比喻,但是不能太死扣字面的意思。因为对于部分函子来说,比喻成盒子虽然不错但是还是不够贴切。一个更具合适的术语是 计算的上下文。 这上下文可以是此运算可能是一个值也可能会失败(Maybe a和Either a),也可以是表示可能由很多的值(list)等等诸如此类。
如果我们希望针对一个函子的实例来制作类型构建方法,它应该要包括诸如 * -> *。也就是说要至此将一个具体的类做为创建的参数。 比如Maybe能够实例化就是因为它可以根据类型参数来产生具体的实例类型,就像Maybe Int 或者Maybe String。 如果一个类型构建方法比如 Either由两个类型参数,我们必须逐个赋予类型进行构造直到最后一个。所以我们不能写instance Functor Either where, 但 instance Functor (Either a) where就可以。只需要想象 fmap是针对具体类型的 Either a, 而对应的类型声明就是 fmap :: (b -> c) -> Either a b -> Either a c。如你所见在这里 Either a是固定的, 因为Either a 只需要一个类型参数。如果直接是带两个参数的 Either fmap :: (b -> c) -> Either b -> Either c 就会没有实际意义。

我们已经学习了很多 Functor的类型(实际上是类型构造器),比如 [], Maybe, Either a 以及一个我们手工自制的 Tree。 我们也看到了函数们是如何通过这些函子进行映射。而在这一节,我们要看两个新的函子实例 IO 和 (->) r。

如果一个值有着类似 IO String的类型就因为着它是一个IO动作。当它执行是会去现实世界获取一个字符串,并把字符串当作结果返回出来。 在do语法中通过<-可以把这个结果绑定到一个名称。 我们可以想象I/O动作是一个长了脚的盒子,它可以爬到外面的世界中去为我们获取一些值。我们可以观察它取回来的东西,但是我们的观察记录/分析结果都还得在放回到 IO盒子里。 通过长脚的盒子这个比喻我们可以看到 IO的行为是符合函子的。

具体来看看为什么IO是 Functor的一个实例。当通过 fmap 映射一个函数到 I/O 动作, 我们希望的是首先I/O动作得到一个值,然后我们的函数也被应用到这个结果值上。

instance Functor IO where
fmap f action = do
result <- action
return (f result)
在I/O动作上映射一个动作的结果仍然是一个I/O动作,所以随即我们就用 do语法来将两个动作结合称为一个新的动作。 在这个 fmap的实现中,我们首先执行原始的I/O动作并把这个结果命名为 result。 接着在执行 return (f result)。而你们也知道这个 return方法只是让一个I/O动作呈现其结果而不做其他事情。 由do语法块生成的动作总是会把最后一个动作的值做为整体的结果。这样也说明为什么我们使用return制作一个新的I/O动作并不真的做什么,只是 f result做为新动作的返回值提供出来。

我们可以来实践一下以便有一些直观的感觉。它真的很简单,就比如下面这段代码:

main = do line <- getLine
let line' = reverse line
putStrLn $ "You said " ++ line' ++ " backwards!"
putStrLn $ "Yes, you really said" ++ line' ++ " backwards!"
用户输入一行而我们在逆序之后再返回给用户。下面是使用 fmap来重构的代码:

main = do line <- fmap reverse getLine
putStrLn $ "You said " ++ line ++ " backwards!"
putStrLn $ "Yes, you really said" ++ line ++ " backwards!"
就如之前用 fmap 映射 reverse 到 Just "blah" 从而得到 Just "halb", 我们可以用 fmap 映射reverse 到 getLine。 getLine是一个类型为 IO String的I/O动作, 用reverse来映射后会得到一个去真实世界获取字符串然后应用到reverse再做为结果的I/O动作。 就像我们可以应用函数到在 Maybe盒子里的东西,我们也可以应用函数到在 IO盒子中的东西上,只是此时它会去真实世界获取一些东西。 然后如果我们用 <-把它绑定到一个名称上,这个名称将会反馈的结果就以及是应用过 reverse之后的了。

fmap (++"!") getLine 这个IO动作的行为和 getLine, 区别值在于结果的最后会附加 "!" !

假设 fmap 只是应用于 IO那么它的类型应该如何?它的类型声明应该是 fmap :: (a -> b) -> IO a -> IO b。 fmap会把一个函数和I/O动作结合称为一个新的动作,除非这个函数是应用在它自身包含的结果上。

如果你最终发现自己绑定一个I/O动作到一个名称只是为了再将结果传入到另外一个函数中,可以考虑使用 fmap 这样会更加幽雅。 如果你准备对函子中的数据进行多次转换,可以使用lambda或者别的合适的方法在顶层声明专门的函数来完成函数的组合:

import Data.Char
import Data.List

main = do line <- fmap (intersperse '-' . reverse . map toUpper) getLine
putStrLn line
$ runhaskell fmapping_io.hs
hello there
E-R-E-H-T- -O-L-L-E-H
你应该可以看懂吧, intersperse '-' . reverse . map toUpper意味着这个函数会传入一个字符串然后依次应用到 toUpper, reverse和 intersperse '-'上。 它也可以写成 (\xs -> intersperse '-' (reverse (map toUpper xs))),只是这样写就不好看了。

另外还有一个我们一直在使用的但是并不知道它是Functor的实例就是(->) r。 现在你可能有些疑惑,这个 (->) r算什么鬼东西啊?就如同可以把 2 + 3 写成 (+) 2 3 一样, 可以把函数类型 r -> a 重写成为(->) r a的形式。 当我们把它变形为 (->) r a就可以换一个角度来看 (->)。 此时可以比较直观的看到它是一个和 Either一样的带两个类型参数的类型构造器。 不过需要记住的是我们已经说过一个要成为 Functor的实例它的类型构造器只能到一个类型参数。 所以我们不能把(->) 变成 Functor的一个实例,但是将它部分应用变成 (->) r后就不再有任何问题。 如果语法允许分成几次对类型构造器进行赋值(比如可以对 +进行部分赋值为 (2+)也等同于 (+) 2), 那你就可以用 (->) r来代替 (r ->)。那函子函数又如何呢?让我们来看看它的在 Control.Monad.Instances中的实现吧。

我们通常把传入任何值之后又传出任何值的函数定义为 a -> b。r -> a是完全相同的,只不过是使用的代表类型参数的字母不同。
instance Functor ((->) r) where
fmap f g = (\x -> f (g x))
如果语法允许,我们可以重构代码为如下

instance Functor (r ->) where
fmap f g = (\x -> f (g x))
但是并不可以,只能使用前面那种风格的写法。

首先让我们来看看 fmap的类型声明, fmap :: (a -> b) -> f a -> f b。然后我们可以在脑海中把代表函子的 f替换成为 (->) r。 我们可以来看看fmap的行为在此时实际上是怎样的。 它已经变成了 fmap :: (a -> b) -> ((->) r a) -> ((->) r b), 然后我们还可以把 (->) r a 和 (->) r b 写成平时函数那样的顺序 r -> a 和 r -> b。 最终就成为了fmap :: (a -> b) -> (r -> a) -> (r -> b)。

嗯简单的说呢,把一个函数映射到一个函数得到一个函数,就如同的把一个函数映射到一个 Maybe得到一个 Maybe或者把一个函数映射到 list得到一个 list。fmap :: (a -> b) -> (r -> a) -> (r -> b) 可以说明什么呢?它传入一个 a到 b的函数,再传入一个 r到 a函数,最后返回一个 r到 b的函数。 是否有让你想起些什么?是的,就是函数组合!而这个函数组合做的就是们把 r -> a的输出做为 a -> b的输入,连接生成新的函数 r -> b。 如果你去看下面这种实现的定义,你就会发现真的只是函数组合而已。另外一种实现方式如下:

instance Functor ((->) r) where
fmap = (.)
这就揭露了用 fmap映射到函数 只是按照顺序组合的实质。 使用 :m + Control.Monad.Instances命令来加载相关的定义后就可以自己来试试映射到函数了。

ghci> :t fmap (*3) (+100)
fmap (*3) (+100) :: (Num a) => a -> a
ghci> fmap (*3) (+100) 1
303
ghci> (*3) fmap (+100) $ 1
303
ghci> (*3) . (+100) $ 1
303
ghci> fmap (show . (*3)) (*100) 1
"300"
我可以把fmap写成中缀函数,这样它和.的相似之处就很明显了。 在输入的第二行我们把 (*3)映射到 (+100)上,这个函数会先调用 (+100)然后在把结果传入到 (*3),最后返回最终的结果。我们对这个组合后的函数传入参数 1。

学 Haskell 应该走哪几个过程? by jiyinyiyong

测试帖.. 发现预览功能和标签和 CNode 上对比是不是 bug
Uncaught ReferenceError: $ is not defined Chrome 上报错

搭上自我介绍.. 小弟大三, 学不好数学和 C, 于是在网上翻资料想学玩代码. Linux 党.
入门的时候绕几种语言, 后来为了语法去熟悉了 Haskell, 没能深入..
接触函数式编程后 CoffeeScript 就感觉顺多了, 就很浅地开始玩 Node

特别喜欢的还是 Haskell, 但一直都有种太高无法接近的感觉
所以特别想了解开始学 Haskell, 基础语法之后, 怎样开始深入学和写实用的代码?
还有比方入门的时候应该从哪些代码进行练习熟悉和掌握?
然后这后面又应该从什么方面继续接触 Haskell 深入的东西?

手把手教你做λ(一)关于λ表达式的一切 by ninegua

(前λ讲堂的学员可以跳过此节)

函数语言追根溯源的话,都是来自于 Alonzo Church 和他的学生 Stephen Kleene 在 1932 年的一篇论文引入的 λ-Calculus (λ演算) 的概念。1936 年 Alan Turing 发表论文引入了 Turing Machine (图灵机) 的概念,奠定了他当代计算机科学之父的地位。于此同时,他也成为 Church 的学生,在普林斯顿攻读博士学位。后来 Kleene 证明了λ演算、图灵机和早先 Kurt Gödel 引入的 general recursion (递归) 函数是等价的,并提出了一个伟大的命题,也就是后来人们说的 Church-Turing Thesis (丘齐图灵命题):任何可计算的东西都能够被这三种方式表达。

当然,可计算 (computable) 并不是一个有着精确定义的概念,这也是为什么丘齐图灵命题是一个无法被证明的 hypothesis (假说)。实际上,Church-Turing Thesis 往往被看作是对“什么是可计算的”一个定义。这个命题的优美之处,还在于它成功地在具象的机器和抽象的代数之间划了一个等号。前者着重执行指令,后者着重于对表达式求值。这也是的命令式语言 (Imperative Language),和函数式语言的 (Functional Language) 的区别。

在一个只学习过命令式语言的程序员看来,Haskell 这类的函数语言实属异类。看似没有清晰的运行流程和分枝结构,既不实用又让人难以理解。其实作为 Haskell 的核心,λ表达式是异常简洁的。下面是关于λ表达式 (λ Expression),也叫λ项 (λ Term),的定义:

  1. 变元是λ项,通常用小写字母写出来,比如 x,y,z 等;
  2. 如果 M 是λ项,那么 λx.M 也是;
  3. 如果 M 和 N 都是λ项,那么 M N 也是。

变元 (variable) 形态的λ项很简单,就是 x 或者 y 或者 z 或者什么别的,我们通常用单个小写字母,有
时候也加上数字脚标,比如 x0, x1, x2 等,以示区分。

抽象 (abstraction) 形态的λ项,写出来就是先用λ这个符号本身作开头,后面跟一个变元,然后一个小点,然后跟一个任意的λ项。例如 λx.x 或 λx.y 或 λx.x x 等。

应用 (application) 形态的λ项,就是两个λ项写在一起,中间仅留一个空格做分隔。例如 f x 或者 x x。 写在前面的λ项通常叫函数(function),后面的叫参数(argument)。比如在 x x 这个表达式里,就是把 x 这个函数作用于(apply to)它自己。

在实际书写抽象和应用两种λ项时,如果没有一定的标识,往往会产生歧义。所以通常是用括号来把一个λ项和它前后的符号区分开,比如 (λx.x) y 这个表达式,就是说函数 λx.x 作用在参数 y 上。
我们通常不认为括号本身是λ项的一部分,使用它们纯粹是为了书写。

括号的使用有时候也可以略去。约定俗成的习惯是在表达抽象时,小点后面整个式子(除非是遇到反括号)
都是与小点前面的变元对应的λ项。比如 λx.(λy.(x y)) 就可以简写为 λx.λy.x y;在表达应用时则向左结
合,比如 f x y 应该被理解为 (f x) y 而不是 f (x y)。

习题

以下哪些是正确的λ项?

  1. a b c d e f
  2. (g h) (i j)
  3. λx.(x+1)
  4. λx.f (λy.f y)
  5. λ(x y).y
  6. λ(λx.x).y
  7. (λf.f x y) (λx.λy.x)
  8. λx.y.x
  9. f g λx.y z

手把手教你做λ(三)漂亮打印与数学归纳法 by ninegua

在上一节,我们定义了以下的数据类型,用来在 Haskell 程序里表达一个λ项:

 data Term = Var Var | Lam Var Term | App Term Term 
 data Var  = V String

虽然这种内部表达形式和λ项的结构一致,但它并不利于阅读和书写,而且关于 Term 的表达式只能写在程序里面。所以接下来我们要解决的问题,就是如何在内部数据类型和书面的字串之间转换,也即关于λ项的输入和输出的问题。

先讲输出。从数据结构转换成字串输出的过程,有个好听的名字叫 Pretty Print。在 Haskell 里,我们通常可以用 show 函数来做这件事情。例如:show 123 的结果是字串 "123",而 show pi 的结果是 "3.141592653589793"。我们可以对 TermVar 也自行定义 show 函数来做 Pretty Print,比如:

 instance Show Var where
   show (V s) = s

关于 Var 类型的 show 函数非常简单,直接使用在 Var 类型的定义里代表变元名字的那个字串就可以了。而对 Term 定义 show 函数就略为复杂一点:

 instance Show Term where
   show (Var v)   = show v
   show (Lam x e) = "λ" ++ show x ++ "." ++ show e
   show (App f e) = show f ++ " " ++ show e

这里我们针对 Term 类型的每种构造形式分别进行处理:

  • 对变元结构直接用 show v,因为这里 vVar 类型,而我们已经定义了 Var 类型的 show 函数。
  • 对函数结构则用 "λ" 和 "." 符号把型参变元 x 和函数体 e 连接起来,其中对于 ve 都使用 show 来把它们分别转换成字串;
  • 对应用结构则用空格把函数部分 f 和参数部分 e 隔开就好了。

在前一节,我们已经注意到定义 Term 类型后两种结构时,也同样用到了 Term 类型本身。这种递归结构几乎是以同样的形态出现在 show 函数的定义里:例如,为了得到 Lam x e 里面子结构 e 的字串,我们直接使用 show e,用 show 函数本身来定义 show 就是递归。

我们在中学数学里学习过自然归纳法,它把关于自然数 n 的证明,归结成两种情况:

  1. 当 n = 1 时证明命题成立。
  2. 假设 n = k 时命题成立,证明 n = k + 1 时命题同样成立。

实际上,自然归纳法借助了自然数的内部结构,也即

  • Base case (基础):1 是自然数。
  • Inductive case (归纳):假如已知 k 为自然数,那么 k + 1 也是自然数。

同样在 Term 的定义里,我们也能够观察到类似的情况:

  1. Var Var 是基础: 如果 v :: Var 那么 Var v :: Term。这里尽管我们有一个 v 属于 Var 类型的前提,但它不是递归。
  2. Lam Var Term 是归纳:如果 v :: Vare :: Term,那么 Lam v e :: Term。这里对于 e 的类型所作的前提假设是递归。
  3. App Term Term 也是归纳:如果 f, e :: Term,那么 App f e :: Term。这里对于 fe 的类型所作的前提假设是递归。

对数据类型采用 base case 和 inductive case 的定义方式叫做归纳定义 (inductive definition)。我们回过头看关于 Termshow 函数,它的定义同样也是归纳定义:

  1. show (Var v) 是基础:已知 show v,将变元结构的 Term 转换为字串。
  2. show (Lam v e) 是归纳:已知 show vshow e,将函数结构的 Term 转换为字串。
  3. show (App f e) 也是归纳:已知 show fshow e,将应用结构的 Term 转换为字串。

作为程序员,我们可能更熟悉遍历 (traversal) 这个概念,把 show 函数看作是对一个有着递归结构的数据进行遍历操作,在每个节点计算它所对应的字串。有趣的是,在这里我们又一次成功地把具象的操作和抽象的数学归纳法对应起来了。

而作为细心的程序员的你,一定也早就发现上面定义的 show 函数有个 bug:它没有考虑到用字串书写λ项可能会产生的歧义。比如以下两个表达式的计算结果都是字串 "λx.x x"

  • show (Lam (V "x") (App (Var (V "x")) (Var (V "x"))))
  • show (App (Lam (V "x") (Var (V "x"))) (Var (V "x")))

而它们完全是两种结构。为了消除歧义,我们可以在 Pretty Print 时加入括号。更正后的 show 函数如下:

 instance Show Term where
   show (Var v)   = show v
   show (Lam x e) = "(λ" ++ show x ++ "." ++ show e ++ ")"
   show (App f e) = "(" ++ show f ++ " " ++ show e ++ ")"

这样一来,上面两个例子的结果分别是 "(λx.(x x))""((λx.x) x)",就不会产生歧义了。

###习题

一、如果把上面的 show 函数看作是对 Term 的遍历操作,那它是先序 (pre-order) 还是后序 (post-order)?你能用另一种方法实现 show 函数吗?

二、上面的 show 函数作为 Pretty Print 其实还不够漂亮,因为括号用的太多了。比如 (λx.(x x)),考虑到 . 的作用范围是到最右边,可以简写为 λx.x x;而如果是 ((f x) y),考虑到应用结构是左结合 (left associative),可以化简为 f x y。你能实现一个新的 show 函数,让它的结果包含最少的括号吗?

三、Haskell 同样可以定义那些没有 base case 只有 inductive case 的数据类型,这叫 co-inductive definition。请举一个切实的例子吧!

惰性计算有可能应用于取列表长度的计算吗? by Liutos

我试过在GHCi中输入下面的表达式

1 > length [1, 2..]

系统似乎会首先计算出无限列表的长度再来和1进行比较。有没有这么一种惰性,可以让length在比较时尽量少地计算的吗?还是说,这已经不是惰性的对付范围了,而是另外范畴的概念了?

SPJ 讲cloud haskell(需翻墙) by codeplayer

http://skillsmatter.com/podcast/home/haskell-cloud/js-4179

要点:

  1. erlang in haskell。
  2. 分布式计算不同范式适合解决不同问题,最好一个语言同时支持各种范式。GHC已经实现了N种,现在再把erlang给实现掉。
  3. first-class typed channel,一个进程可以new多个channel,send
    end-point可以传给其他进程,receive end-point不可以。
  4. 序列化函数,如何处理闭包是难点,需要语言稍微扩展一下。(目前的cloud haskell实现通过template haskell也可以做到)。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.