Some languages support a special "do notation" that allows to express monadic 
operations more naturally.

However, there is an even more powerful option, that I'd call "in notation" (I 
came up with it, but it's obvious, so I'm sure there's some language that has 
something like it).

The idea is that we add an "in" keyword, and in-types, which are the type "in 
T" where T is a monadic type.

In-types are physically represented as the monadic type, but semantically 
behave like the type contained in the monad; they are constructed with the 
expression "in x".

The "out" keyword does the opposite, converting an in-type to the wrapped type.

Operations performed on in types actually act on the value inside of the monad, 
as better specified below

Quick examples:
out(in Some(3) + 6) gives Some(9)
out(in Some(3) + in Some(6)) also gives Some(9)
out(in Some(3) + in None) gives None
out(in ~[1, 2, 3] + 10) gives ~[11, 12, 13]
out(in ~[1, 2, 3] + in ~[10, 20, 30]) gives ~[11, 21, 31, 12, 22, 32, 13, 23, 
33]

Internally, when the compiler encounters any expression including an in-type 
(expressions include control flow constructs), it proceeds like this 
(considering its operands from left to right):
1. Optionally, if the expression is correctly typed (e.g. calling a function 
that takes an in-type), it compiles it normally
2. If the expression does not have an in-type value itself, it converts the 
expression into a closure, and passes it to map()
2. If the expression does have an in-type value itself (for example in x + in y 
has an in-type when viewed as a function of in x, due to the presence of in y), 
it converts out(expression) into a closure, and passes it to flat_map()

Non-movable control flow like return or break is forbidden in expression 
involving in-types (they can be implemented with a flag, but that causes other 
equivalence issues).

The advantage of this is that special do-notation syntax is not required, and 
one can naturally manipulate the values.

The disadvantage is that it adds a layer of "magic", making what the code 
actually does less obvious.

This is particularly true with list-like monads, where simple things like (in 
x) + (in y) actually become a double loop with quadratic run-time; this can be 
optionally avoided by only allowing "in notation" to be used with monads that 
call the closure once.

Also, side effects will be performed a different amount of times depending on 
the monad values, which may also not be obvious.

Note that there are a lot of details that I omitted for the sake of brevity, 
and would need to be handled.                                        
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to