Haskell 是否真正纯粹(是否有任何语言处理系统外的输入和输出)? [英] Is Haskell truly pure (is any language that deals with input and output outside the system)?

查看:17
本文介绍了Haskell 是否真正纯粹(是否有任何语言处理系统外的输入和输出)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在涉及函数式编程方面的 Monads 之后,该功能是否真的使语言变得纯粹,或者只是在黑板数学之外的现实世界中推理计算机系统的另一张越狱卡"?

After touching on Monads in respect to functional programming, does the feature actually make a language pure, or is it just another "get out of jail free card" for reasoning of computer systems in the real world, outside of blackboard maths?

这不是有人在这篇文章中所说的火焰诱饵,而是一个真正的问题,我希望有人可以击倒我并说,证明,它是纯粹的.

This is not flame bait as someone has said in this post, but a genuine question that I am hoping that someone can shoot me down with and say, proof, it is pure.

此外,我正在研究与其他不那么纯粹的函数式语言和一些使用良好设计并比较纯度的面向对象语言的问题.到目前为止,在我非常有限的 FP 世界中,我还没有深入了解 Monads 纯度,你会很高兴知道但我确实喜欢不变性的想法,这在纯度赌注中更为重要.

Also I am looking at the question with respect to other not so pure Functional Languages and some OO languages that use good design and comparing the purity. So far in my very limited world of FP, I have still not groked the Monads purity, you will be pleased to know however I do like the idea of immutability which is far more important in the purity stakes.

推荐答案

采用以下迷你语言:

data Action = Get (Char -> Action) | Put Char Action | End

Get f 表示:读取一个字符c,并执行动作f c.

Get f means: read a character c, and perform action f c.

Put c a 表示:写入字符c,执行动作a.

Put c a means: write character c, and perform action a.

这是一个打印xy"的程序,然后要求输入两个字母并以相反的顺序打印它们:

Here's a program that prints "xy", then asks for two letters and prints them in reverse order:

Put 'x' (Put 'y' (Get (a -> Get ( -> Put b (Put a End)))))

您可以操作此类程序.例如:

You can manipulate such programs. For example:

conditionally p = Get (a -> if a == 'Y' then p else End)

这是类型 Action ->动作 - 它需要一个程序并给出另一个首先要求确认的程序.这是另一个:

This is has type Action -> Action - it takes a program and gives another program that asks for confirmation first. Here's another:

printString = foldr Put End

这有类型 String ->动作 - 它接受一个字符串并返回一个写入字符串的程序,如

This has type String -> Action - it takes a string and returns a program that writes the string, like

Put 'h' (Put 'e' (Put 'l' (Put 'l' (Put 'o' End))) .

Haskell 中的 IO 工作方式类似.尽管执行需要执行副作用,但您可以以纯粹的方式构建复杂的程序而无需执行它们.您正在计算程序的描述(IO 操作),而不是实际执行它们.

IO in Haskell works similarily. Although executing it requires performing side effects, you can build complex programs without executing them, in a pure way. You're computing on descriptions of programs (IO actions), and not actually performing them.

在像 C 这样的语言中,您可以编写一个函数 void execute(Action a) 来实际执行程序.在 Haskell 中,您可以通过编写 main = a 来指定该操作.编译器创建了一个执行动作的程序,但你没有其他方法来执行一个动作(除了肮脏的技巧).

In language like C you can write a function void execute(Action a) that actually executed the program. In Haskell you specify that action by writing main = a. The compiler creates a program that executes the action, but you have no other way to execute an action (aside dirty tricks).

显然GetPut 不仅仅是选项,您还可以为IO 数据类型添加许多其他API 调用,例如操作文件或并发.

Obviously Get and Put are not only options, you can add many other API calls to the IO data type, like operating on files or concurrency.

添加结果值

现在考虑以下数据类型.

Now consider the following data type.

data IO a = Get (Char -> Action) | Put Char Action | End a

前面的Action类型相当于IO(),即一个总是返回unit"的IO值,相当于void".

The previous Action type is equivalent to IO (), i.e. an IO value which always returns "unit", comparable to "void".

这个类型和Haskell IO很相似,只是在Haskell IO中是一个抽象数据类型(你不能访问定义,只能访问一些方法).

This type is very similar to Haskell IO, only in Haskell IO is an abstract data type (you don't have access to the definition, only to some methods).

这些是可以以某种结果结束的 IO 操作.像这样的值:

These are IO actions which can end with some result. A value like this:

Get (x -> if x == 'A' then Put 'B' (End 3) else End 4)

类型为IO Int,对应一个C程序:

has type IO Int and is corresponding to a C program:

int f() {
  char x;
  scanf("%c", &x);
  if (x == 'A') {
    printf("B");
    return 3;
  } else return 4;
}

评估和执行

评估和执行是有区别的.您可以评估任何 Haskell 表达式,并获得一个值;例如,将 2+2 :: Int 计算为 4 :: Int.您只能执行类型为 IO a 的 Haskell 表达式.这可能有副作用;执行 Put 'a' (End 3) 将字母 a 放到屏幕上.如果您评估 IO 值,如下所示:

There's a difference between evaluating and executing. You can evaluate any Haskell expression, and get a value; for example, evaluate 2+2 :: Int into 4 :: Int. You can execute Haskell expressions only which have type IO a. This might have side-effects; executing Put 'a' (End 3) puts the letter a to screen. If you evaluate an IO value, like this:

if 2+2 == 4 then Put 'A' (End 0) else Put 'B' (End 2)

你得到:

Put 'A' (End 0)

但是没有副作用 - 您只进行了评估,这是无害的.

But there are no side-effects - you only performed an evaluation, which is harmless.

你会怎么翻译

bool comp(char x) {
  char y;
  scanf("%c", &y);
  if (x > y) {       //Character comparison
    printf(">");
    return true;
  } else {
    printf("<");
    return false;
  }
}

转化成IO值?

修复一些字符,比如v".现在 comp('v') 是一个 IO 操作,它将给定的字符与 'v' 进行比较.类似地,comp('b') 是一个 IO 操作,它将给定的字符与 'b' 进行比较.一般来说,comp 是一个函数,它接受一个字符并返回一个 IO 动作.

Fix some character, say 'v'. Now comp('v') is an IO action, which compares given character to 'v'. Similarly, comp('b') is an IO action, which compares given character to 'b'. In general, comp is a function which takes a character and returns an IO action.

作为 C 语言的程序员,您可能会争辩说 comp('b') 是一个布尔值.在 C 中,求值和执行是相同的(即它们意味着相同的事情,或者 同时发生).不在 Haskell 中.comp('b') 评估为一些IO动作,在执行后给出一个布尔值.(准确地说,它像上面一样计算为代码块,只是用 'b' 代替了 x.)

As a programmer in C, you might argue that comp('b') is a boolean. In C, evaluation and execution are identical (i.e they mean the same thing, or happens simultaneously). Not in Haskell. comp('b') evaluates into some IO action, which after being executed gives a boolean. (Precisely, it evaluates into code block as above, only with 'b' substituted for x.)

comp :: Char -> IO Bool
comp x = Get (y -> if x > y then Put '>' (End True) else Put '<' (End False))

现在,comp 'b' 计算结果为 Get (y -> if 'b' > y then Put '>' (End True) else Put '<'(结尾为假)).

Now, comp 'b' evaluates into Get (y -> if 'b' > y then Put '>' (End True) else Put '<' (End False)).

它在数学上也很有意义.在 C 中,int f() 是一个函数.对于数学家来说,这没有意义——一个没有参数的函数?函数的重点是接受参数.一个函数 int f() 应该等价于 int f.不是,因为 C 中的函数混合了数学函数和 IO 操作.

It also makes sense mathematically. In C, int f() is a function. For a mathematician, this doesn't make sense - a function with no arguments? The point of functions is to take arguments. A function int f() should be equivalent to int f. It isn't, because functions in C blend mathematical functions and IO actions.

头等舱

这些 IO 值是一流的.就像你可以有一个整数元组列表的列表 [[(0,2),(8,3)],[(2,8)]] 你可以用 IO 构建复杂的值.

These IO values are first-class. Just like you can have a list of lists of tuples of integers [[(0,2),(8,3)],[(2,8)]] you can build complex values with IO.

 (Get (x -> Put (toUpper x) (End 0)), Get (x -> Put (toLower x) (End 0)))
   :: (IO Int, IO Int)

一组 IO 操作:首先读取一个字符并将其打印为大写,然后读取一个字符并返回小写.

A tuple of IO actions: first reads a character and prints it uppercase, second reads a character and returns it lowercase.

 Get (x -> End (Put x (End 0))) :: IO (IO Int)

一个 IO 值,它读取一个字符 x 并结束,返回一个将 x 写入屏幕的 IO 值.

An IO value, which reads a character x and ends, returning an IO value which writes x to screen.

Haskell 具有特殊功能,可以轻松操作 IO 值.例如:

Haskell has special functions which allow easy manipulation of IO values. For example:

 sequence :: [IO a] -> IO [a]

它接受一个 IO 动作列表,并返回一个依次执行它们的 IO 动作.

which takes a list of IO actions, and returns an IO action which executes them in sequence.

单子

Monads 是一些组合子(如上面的conditionally),它允许您更结构化地编写程序.有一个由 type 组成的函数

Monads are some combinators (like conditionally above), which allow you to write programs more structurally. There's a function that composes of type

 IO a -> (a -> IO b) -> IO b

给定 IO a 和函数 a -> IO b,返回类型为 IO b 的值.如果您将第一个参数编写为 C 函数 af() 并将第二个参数编写为 bg(ax),它将返回一个程序 g(f(x)).鉴于上面Action/IO的定义,您可以自己编写该函数.

which given IO a, and a function a -> IO b, returns a value of type IO b. If you write first argument as a C function a f() and second argument as b g(a x) it returns a program for g(f(x)). Given above definition of Action / IO, you can write that function yourself.

注意 monad 对纯度不是必需的 - 你总是可以像我上面那样编写程序.

Notice monads are not essential to purity - you can always write programs as I did above.

纯度

关于纯度的本质是参照透明,区分评估和执行.

The essential thing about purity is referential transparency, and distinguishing between evaluation and execution.

在 Haskell 中,如果你有 f x+f x,你可以用 2*f x 替换它.在 C 中,f(x)+f(x) 通常与 2*f(x) 不同,因为 f 可以在屏幕上打印一些东西,或者修改x.

In Haskell, if you have f x+f x you can replace that with 2*f x. In C, f(x)+f(x) in general is not the same as 2*f(x), since f could print something on the screen, or modify x.

由于纯度,编译器有更多的自由,可以更好地优化.它可以重新排列计算,而在 C 中它必须考虑这是否会改变程序的含义.

Thanks to purity, a compiler has much more freedom and can optimize better. It can rearrange computations, while in C it has to think if that changes meaning of the program.

这篇关于Haskell 是否真正纯粹(是否有任何语言处理系统外的输入和输出)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆