-
Notifications
You must be signed in to change notification settings - Fork 156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Let's play with monads #426
Comments
See Fantasy-Land Also, you can sort of have Haskell's "do notation": stuff = do # User
data <- get(url).chain # Promise String
[user, passwd] <- validate-login(JSON.parse data).chain # Validation Error [String, String]
user-data <- do-login(user, passwd).chain # Validation Error, User
return user It would be cool to be able to define arbitrary binary operators though, the following could work: operator a >>= f = a.chain f
operator a <|> b = a.get-or-else b Instead of defining the precedence of things by |
We can use fay for it https://github.com/faylang/fay/wiki |
Do syntax for promises would be fantastic.
|
+1. This would be great! I've been playing with monads in LiveScript recently. With backcalls the code looks obviously much nicer than in vanilla JS. Even with the piping operators you get nice composition. But for the "do" notation, it would be great to have an operator to replace add = (x,y) --> x+y
List = do ->
unit = (a) -> [a]
bind = flip concat-map
lift = (f, ma) --> bind ma, (a) -> unit f a
{unit, bind, lift}
# "do"
let {unit, bind} = List
x <- bind [1]
y <- bind [2 3]
z <- bind [4 5 6]
unit x+y+z #=> [7 8 9 8 9 10]
# piping
let {unit, lift} = List
unit 1 |> lift add 2 |> lift add 3 #=> [6] |
That is a pretty excellent workaround. Perhaps it might be bit much, but On 26 November 2013 06:33, elclanrs notifications@github.com wrote:
|
@alexkalderimis: Unfortunately, |
Why unfortunately ? We need our lower-or-equal than :p. We could see |
/facepalm/ of course it is. I agree that Monads are a bit of sledge-hammer to crack a nut for lists On 26 November 2013 14:06, Nami-Doc notifications@github.com wrote:
|
@alexkalderimis do you even lift? :P |
Monads will be used for defining an context. For instance we can define a chain of functions const func = getUrl >>=
getContentByUrl >>=
sendContentToServer >>=
getResponseFromServer >>=
showResult
func \google.com.ua //Server has processed the content
successfully Each of these functions returns future result or immediate. Actually it is I cannot do it using piping. It just reverted composition. I have solved my task by using simple array of functions const actions =
* getUrl
* getContentByUrl
* sendContentToServer
* getResponseFromServer
* showResult
const go = (arr, cur-val)--> ....//each function returns Promise and I add
observer of result here
go actions, null But looks like it is common issue. And we should have special tool for that |
After experimenting much with LiveScript and monads I came up with another maybe better solution, where the monadic value is fluent = (f) -> ->
f ...
@
Maybe =
unit: fluent (@x) ->
'>=': (f) ->
| @isNothing => @nothing!
| @isJust => f.call @, @x
nothing: fluent (@x=null) ->
@isNothing = true
@toString = -> "Nothing"
just: fluent (@x) ->
@isJust = true
@toString = -> "Just: #{@x}"
valueOf: -> @x
add = (x, y) -->
| x? and y? => Maybe.just x+y
|_ => Maybe.nothing!
# Just
result = do
x <- add 1,2 .\>=
y <- add x,2 .\>=
@unit x+y
console.log result.toString! #=> Just: 8
# Nothing
result = do
x <- add 1,2 .\>=
y <- add x,null .\>=
@unit x + y
console.log result.toString! #=> Nothing |
We're really limited by the fact we have Thoughts ? Ops ? |
Using an example from my parser combinator library: between = (open, close, p) --> (state) ->
do-monad Either
[s1, _] <<= open state
[s2, a] <<= (backtrack state) p s1
[s3, _] <<= (backtrack state) close s2
return [s3, a] Would compile to: between = (open, close, p) --> (state) ->
open(state).chain([s1, _]) ->
backtrack(state)(p s1).chain([s2, a]) ->
backtrack(state)(p s2).chain([s3, _]) ->
Either.of [s3, a] Basically, all of the Similarly, Maybe 1 >>= (a) -> Maybe (a + 1) Would translate to: Maybe(1).chain (a) -> Maybe (a + 1) Though I don't see as much use for Edit: The operations are defined in Fantasy Land, there was a Sweet.js macro for this by @puffnfresh (?), though I can't remember the link. |
@robotlolita monadic bind macro, right here: https://github.com/puffnfresh/sweet-fantasies |
@puffnfresh that macro looks great. Will try it out. |
@elclanrs awesome, we have a few bugs which need to be sorted out: fantasyland/sweet-fantasies#5 |
Isn't the current syntax enough? I see absolutely no reason for new language features when we can work around the syntax a little bit. I'm going to implement haskell IO as an example, sorry in advance for the long post ahead: What this function('liveify') does is take a normal, Haskell-ish monad constructor function, and convert it so that it works in the livescript do <- chain syntax. It returns a function that takes the function's normal args along with a callback(possibly), and apply the callback to the function's result(or bind it, in this case). liveify = (fn) ->
| fn.length == 0 => # in the case of something like readLn
(callback = ->) -> fn!bind callback
| otherwise =>
(...args) ->
| args.length == fn.length => fn ... # in the case of no callback
| otherwise => let [...init, last] = args
# the magic of monads is preserved: the value is passed along with 'bind'
(fn ...init).bind last We can write our monad simply: class IO
(@val) ~>
# we 'liveify' unit so that it can be used nicely in the do <- notaiton
@unit = liveify @@
bind: (callback) -> callback @val We must 'liveify' the monadic functions so they work with our syntax, otherwise they are super simple putStrLn = liveify (msg) -> IO.unit alert msg
readLn = liveify (msg) -> IO.unit prompt msg You can essentially drop this code right into Haskell. Feel like haskell yet? main = do
x <- readLn 'enter a number!'
putStrLn "you entered: #x"
y <- readLn 'enter another number!'
putStrLn "you entered: #y"
putStrLn "I added them up for you: #{parseInt(x) + parseInt(y)}" Note that lines without a 'variable <- expression' statement don't continue the function nesting, and so aren't strictly proper, this can become a problem with promises and such. If needed, this can be fixed by prepending an empty '<-' to those lines: main = do
x <- readLn 'enter a number!'
<- putStrLn "you entered: #x"
... This isn't, strictly speaking, how Haskell would interpret this. It would be closer to make main a function, but that is really simple, plus, it returns the value! How convenient. Also, it hints that main is a monadic function with the extra arrow(the 'do' is not really needed anymore). main = do ->
x <- readLn 'enter a number!'
putStrLn "you entered: #x"
y <- readLn 'enter another number!'
putStrLn "you entered: #y"
putStrLn "I added them up for you: #{parseInt(x) + parseInt(y)}" tl;dr Given 'liveify', it is trivial to implement monads and make implementations such as Maybe, Promises, etc. 'Liveified' functions even work outside of the do statement, just like normal ones! |
In other words operator >>= should be a syntax sugar of calling bind method of current object
We can consider monads like piping, but
It is cool to don't care about scheduling of tasks, defining listeners and checking bad responses. We can define specific monads which can do all such work in background
The text was updated successfully, but these errors were encountered: