'Sequential ordering of evaluation in Haskell
To obtain a sequential evaluation in a functional context, in JavaScript, I frequently use
const right = a => b => b;
const f = () => right(console.log("hi"))(true);
console.log(f());
This is somewhat similar to do thread and much simpler to implement but only works in eager-evaluation languages such as JavaScript.
In Haskell, since the language works in a lazy-evaluation/call-by-need manner, the argument of a will not be evaluated because it's not required in the lambda expression body.
So, I wonder what would be the simple smart approach to implement the right function that works in the lazy evaluation. <- This is my #1 Question here.
There are Haskell wiki articles:
Sequential ordering of evaluation
The
seqprimitive, despite its name, is not required to evaluate its parameters in some predefined order, according to the Haskell 2010 Report. For parallel programming, sometimes a variant of seq is needed with such an ordering:
While we focus mainly on the implementation, our work has had some impact on the programming model: we identify the need for pseq as well as seq (Section 2.1), and we isolated a signficant difficulty in the "strategies" approach to writing parallel programs (Section 7). Runtime Support for Multicore Haskell; Simon Marlow, Simon Peyton Jones and Satnam Singh. Instead of two or more similar primitives with similar purposes, extend the existing seq primitive with the traditional ordering of evaluation for its parameters - first, then second; with the role of simple strictness-control being performed by a new primitive with a more appropriate name e.g.
amid, in Strictness without ordering, or confusion.
and
Strictness without ordering, or confusion
As the Haskell 2010 Report does not specify any order of evaluation with respect to its parameters, the name of the primitive
seq :: a -> b -> bis a misnomer.
Introduce the primitive
amid, with the same (Haskell 2010 Report) requirements:
infixr 0 `amid`
primtive amid :: a -> b -> b
infixr 0 $!
($!) :: (a -> b) -> a -> b
f $! x = x `amid` f x
Actually, the function is exactly the same as my right; however, what I don't understand is as I mentioned in lazy evaluation, since a is not required in the function body, it shall not be evaluated. So what happens here? #2 Question
A code I think is
right = \a -> \b -> (const a . const b) 0
but, I don't know this one stably works.
Solution 1:[1]
The fact that Haskell is lazy and pure has two results that are relevant to your question:
1. You cannot write this function
There is simply no facility in the language that allows you to say "evaluate this, then evaluate that". As you found seq is closest, in that it makes sure that its left argument is evaluated before it returns the right element. But it may evaluate b, then a, then return b if it likes.
2. You do not need this function
Evaluating an expression in Haskell never1 has any observable side effects. So flip const, equivalent to (\x y -> y), has the same behavior as this function you are imagining. It returns its second argument, and you can imagine that it evaluates the left one if you like, which has no effect because evaluating things never does. Your proposed implementation of right is buggy, in that it returns the left argument instead of the right one, but otherwise it has the same behavior again.
In the cases where you do need sequential ordering, it is not ordering of evaluation, but of the effects embedded in IO. Vikstapolis's answer covers this topic well already, so I will go into no further detail.
1 Okay, actually there are plenty of exceptions to this rule. But they are all cheating in some way or another, and do not really factor in here. Some of the most common exceptions:
- Evaluating an expression may allocate memory, or cause some currently-pending work to actually be done. If you need these effects, that's exactly what
seqis for. - Debug.Trace lets you write logs to the console as part of evaluating an otherwise-pure expression. This is only for debug use, so there is no standard-library function for fine-tuning the ordering: if you care about the logs an expression produces, you should really care about the expression itself too. The stuff in Debug.Trace is built on top of
seqalready, so if allleftdoes is log something, you can just use a function from Debug.Trace, such astrace "hi" True. unsafePerformIOcan perform arbitrary IO inside of supposedly-pure expressions. It is a tool reserved for very low-level stuff, shunned even by language experts except in dire need. No beginner should worry about this function.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
