Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell: a return before is cancelled out by a monad after. How?

How do I understand the statement return 1 getLine? It passes type-checking, and seems identical to 1. How are getLine and return "cancelling" each other out? It doesn't make sense to me at all.

Prelude> :t return 1
return 1 :: (Monad m, Num a) => m a

Prelude> :t return 1 getLine            -- why is it not a type error?
return 1 getLine :: Num t => t          

Prelude> return 1 getLine               
1                                       -- whatever happened to getLine?

Also, how come the end product is "pure" even though it involves a getLine?

like image 527
cobra Avatar asked Sep 25 '21 10:09

cobra


People also ask

How does return work in Haskell?

return is actually just a simple function in Haskell. It does not return something. It wraps a value into a monad. Looks like return is an overloaded function.

How do monads work Haskell?

A monad is an algebraic structure in category theory, and in Haskell it is used to describe computations as sequences of steps, and to handle side effects such as state and IO. Monads are abstract, and they have many useful concrete instances. Monads provide a way to structure a program.

Can a Haskell function return nothing?

No. However, you can have functions that return a trivial value. The () type has only one inhabitant, () .

What problem do monads solve?

Conclusion. Monad is a simple and powerful design pattern for function composition that helps us to solve very common IT problems such as input/output, exception handling, parsing, concurrency and other.

Why doesn’t the Haskell monadclass require a function?

That is not an accident. Nothing prevents the monad author from allowing it using functions specific to the monad. For instance, values can be extracted from the Maybemonad by pattern matching on Just xor using the fromJustfunction. By not requiring such a function, the Haskell Monadclass allows the creation of one-way monads.

What is the best order to define return types in Haskell?

although the recommended order is to define return as pure if the two would otherwise end up being the same. Nondeterminism using List monad to represent carrying multiple values In order to improve the look of code that uses monads, Haskell provides a special form of syntactic sugar called do -notation. For example, the following expression:

What is a one-way monad in Haskell?

By not requiring such a function, the Haskell Monad class allows the creation of one-way monads. One-way monads allow values to enter the monad through the return function (and sometimes the fail function) and they allow computations to be performed within the monad using the bind functions >>= and >>,...

What is the best way to support monads in Haskell?

Click hereto see the solution. Monad support in Haskell Haskell's built in support for monads is split among the standard prelude, which exports the most common monad functions, and the Monad module, which contains less-commonly used monad functions.


Video Answer


2 Answers

If you write return 1 getLine, that means that return 1 should be a function that takes getLine as parameter.

We are lucky, because there is an instance for Monad with a function ((->) r) [src]. Indeed:

-- | @since 2.01
instance Applicative ((->) r) where
    pure = const
    (<*>) f g x = f x (g x)
    liftA2 q f g x = q (f x) (g x)

-- | @since 2.01
instance Monad ((->) r) where
    f >>= k = \ r -> k (f r) r

(->) r is a more canonical form for r -> …. It is thus a function with r as parameter type, and if applied to a type parameter a for example, then ((->) r) a is equivalent to r -> a.

For this instance of Monad, return has the same implementation as pure which is const. This thus means that:

return 1 getLine

is equivalent to:

   const 1 getLine
&rightarrow; (\_ -> 1) getLine
&rightarrow; 1
like image 165
Willem Van Onsem Avatar answered Oct 09 '22 13:10

Willem Van Onsem


How do I understand the statement return 1 getLine?

By first noticing that return 1 getLine is actually (return 1)getLine.

This means that return 1 is a function, since it accepts an additional argument, and functions are monadic values too (that's why it's not a type error).

Thus we must unify

return   ::  Monad m   => a  -> m  a
return 1 :: (Monad m, Num a) => m  a
return 1 ::           Num a  => r -> a     -- Monad (r ->)

so that m a ~ r -> a and thus m ~ (->) r (which is the proper way to write (r ->) which is itself an invalid syntax).

For functions, return = const and so we have

return 1 getline 
= const 1 getline 
= const 1 undefined
= 1

because const is defined as

const :: a -> b -> a
const    x    y =  x

(and that's how getLine gets ignored).


In general, the functions Monad instance is defined so that

do { a <- f ; b <- foo a ; return (bar a b) } x

is the same as

let { a = f x ; b = foo a x } in bar a b  -- const (bar a b) x

And that's the reason for return being defined as const.

This means that return and getLine do not "cancel each other out". That's the doing of return only, by virtue of being applied to a second argument, whatever that is, even one which is undefined.

So getLine belonging to the IO "monad", as you write in the question, is irrelevant. Everything is a value in Haskell, and getLine is just another value as well. And it is a pure value at that; it just describes an "impure" I/O action, but by itself it is just another value, simply used as an argument here. Only when getLine appears in main in an appropriate position (or in other code that appears in main, recursively) is it "run", i.e. the computation it describes is actually performed. Here this is not the case.

The monad in play here is the functions monad, to which this return belongs.


As to the purity question, monads are not pure or impure themselves. A type which implements a Monad can be either pure or impure. Functions happen to be pure.

What Monads do is separate the pure from the potentially impure.

But that's actually what Functors do as well. Monads, specifically, allow then for the intermingling chaining of the pure-->impure-->pure-->impure-->.... bits, while preserving their separation (again, the "impure" bits can actually be pure themselves as well; the important thing is the separation between the two "worlds").

like image 43
Will Ness Avatar answered Oct 09 '22 11:10

Will Ness