- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Support for Haskell like do-comprehensions
Sat, 2010-01-02, 20:59
Hello,
I've been playing with Monads in Scala for some time now and I miss the simple syntax of do-comprehensions in Haskell.
Consider for instance this code in Haskell:
do y <- f x
g y
The problem is that the equivalent code in Scala must be written like this:
for { y <- f(x);
z <- g(y) } yield (z)
which adds quite some noise compared to the signal.
What I'd like is support for expressions like this:
for {
y <- f(x);
g(y)
}
which could get translated into:
f(x).flatMap { case y => g(y)}
The rules would be similar to Haskell rules, that is:
- for {e} is translated to:
e
- for {e1; e2; ...; en} is translated to
e1.flatMap {case _ => for {e2, ...; en}}
- for {x <- e1; e2; ...; en} is translated to
e1.flatMap {case x => for {e2; ... en}}
Now I rolled up my sleeves and had a short look in Parsers and TreeBuilder and I'm considering what may be a dirty workaround.
I'm wondering if it would be possible to make the generator accept pattern-less expressions i.e.
Generator ::= Pattern1 (`<-' | '=') Expr [Guard] | Expr [Guard]
and return ValFrom(pos: Position, pat: Option[Tree], rhs: Tree).
I guess that is the hard part but once a pattern-less ValFrom is detected it could trigger the Haskell like for-comprehension generation in expr0, that is:
case FOR =>
atPos(in.skipToken()) {
val (open, close) = if (in.token == LBRACE) (LBRACE, RBRACE) else (LPAREN, RPAREN)
val enums = surround(open, close)(enumerators(), Nil)
newLinesOpt()
if (enums.exists(_.pat.isEmpty) {
makeForLikeHaskell(enums)
} else if (in.token == YIELD) {
in.nextToken()
makeForYield(enums, expr())
} else {
makeFor(enums, expr())
}
I'm not versed in scala compiler nor its plugin mechanism and I'd be very thankful if someone would be so kind as to indicate me the right way to proceed.
Thanks,
Sebastien
I've been playing with Monads in Scala for some time now and I miss the simple syntax of do-comprehensions in Haskell.
Consider for instance this code in Haskell:
do y <- f x
g y
The problem is that the equivalent code in Scala must be written like this:
for { y <- f(x);
z <- g(y) } yield (z)
which adds quite some noise compared to the signal.
What I'd like is support for expressions like this:
for {
y <- f(x);
g(y)
}
which could get translated into:
f(x).flatMap { case y => g(y)}
The rules would be similar to Haskell rules, that is:
- for {e} is translated to:
e
- for {e1; e2; ...; en} is translated to
e1.flatMap {case _ => for {e2, ...; en}}
- for {x <- e1; e2; ...; en} is translated to
e1.flatMap {case x => for {e2; ... en}}
Now I rolled up my sleeves and had a short look in Parsers and TreeBuilder and I'm considering what may be a dirty workaround.
I'm wondering if it would be possible to make the generator accept pattern-less expressions i.e.
Generator ::= Pattern1 (`<-' | '=') Expr [Guard] | Expr [Guard]
and return ValFrom(pos: Position, pat: Option[Tree], rhs: Tree).
I guess that is the hard part but once a pattern-less ValFrom is detected it could trigger the Haskell like for-comprehension generation in expr0, that is:
case FOR =>
atPos(in.skipToken()) {
val (open, close) = if (in.token == LBRACE) (LBRACE, RBRACE) else (LPAREN, RPAREN)
val enums = surround(open, close)(enumerators(), Nil)
newLinesOpt()
if (enums.exists(_.pat.isEmpty) {
makeForLikeHaskell(enums)
} else if (in.token == YIELD) {
in.nextToken()
makeForYield(enums, expr())
} else {
makeFor(enums, expr())
}
I'm not versed in scala compiler nor its plugin mechanism and I'd be very thankful if someone would be so kind as to indicate me the right way to proceed.
Thanks,
Sebastien
Sat, 2010-01-02, 23:57
#2
Re: Support for Haskell like do-comprehensions
Well, it is certainly not that bad but the paper here triggered me.
http://lamp.epfl.ch/~rompf/continuations-icfp09.pdf
"Being forced to use for comprehensions everywhere continuations are accessed does not make for a pleasant programming style, though."
If the people behind Scala feel this way towards for comprehensions then I'm a bit concerned about the reaction of the ones not familiar or averse to FP and I want to reduce the entry barrier as much as possible.
Take for instance the concrete example below [1]. I suppose they mean that something like this doesn't make for pleasant programming:
for ( _ <- suspendWhile(!join);
_ <- exec {
...
};
_ <- Responder.loopWhile(!leave) {
...
for (msg <- getNewMessage;
_ <- exec {
...
}
) yield ()
}
)
I can already imagine all the questions that this example will raise while with the following code, people with imperative background will just feel at home (they won't even miss the semicolon):
for { suspendWhile(!join);
exec {
...
};
Responder.loopWhile(!leave) {
...
for {msg <- getNewMessage;
exec {
...
}
}
}
)
}
If people here think it is not worth the pain, I'll live with it, I'll just go light a candle and pray that people where I work will take it as easy as well :-)
[1] http://svn2.assembla.com/svn/comet-like_scala/Comet-style%20Web%20Applications%20in%20Scala.pdf
2010/1/2 Tony Morris <tonymorris@gmail.com>
http://lamp.epfl.ch/~rompf/continuations-icfp09.pdf
"Being forced to use for comprehensions everywhere continuations are accessed does not make for a pleasant programming style, though."
If the people behind Scala feel this way towards for comprehensions then I'm a bit concerned about the reaction of the ones not familiar or averse to FP and I want to reduce the entry barrier as much as possible.
Take for instance the concrete example below [1]. I suppose they mean that something like this doesn't make for pleasant programming:
for ( _ <- suspendWhile(!join);
_ <- exec {
...
};
_ <- Responder.loopWhile(!leave) {
...
for (msg <- getNewMessage;
_ <- exec {
...
}
) yield ()
}
)
I can already imagine all the questions that this example will raise while with the following code, people with imperative background will just feel at home (they won't even miss the semicolon):
for { suspendWhile(!join);
exec {
...
};
Responder.loopWhile(!leave) {
...
for {msg <- getNewMessage;
exec {
...
}
}
}
)
}
If people here think it is not worth the pain, I'll live with it, I'll just go light a candle and pray that people where I work will take it as easy as well :-)
[1] http://svn2.assembla.com/svn/comet-like_scala/Comet-style%20Web%20Applications%20in%20Scala.pdf
2010/1/2 Tony Morris <tonymorris@gmail.com>
Your Haskell code is equivalent to:
do y <- f x
z <- g y
return z
It's certainly uglier code, but if we can imagine a Haskell that forced
this, which is effectively what Scala does, would you be that much worse
off?
PS: You don't need the parentheses after the yield keyword
Sébastien Bocq wrote:
> Hello,
>
> I've been playing with Monads in Scala for some time now and I miss
> the simple syntax of do-comprehensions in Haskell.
>
> Consider for instance this code in Haskell:
>
> /*do* y <- f x
> g y
> /
> The problem is that the equivalent code in Scala must be written like
> this:
>
> /*for* { y <- f(x);
> z <- g(y) } *yield* (z)
> /
> which adds quite some noise compared to the signal.
>
> What I'd like is support for expressions like this:
>
> /*for* {
> y <- f(x);
> g(y)
> }
> /
> which could get translated into:
>
> /f(x).flatMap { *case* y => g(y)}/
>
> The rules would be similar to Haskell rules, that is:
>
> - /*for* {e}/ is translated to/:
> e
>
> /- /*for* {e1; e2; ...; en} /is translated to/
> e1.flatMap {*case* _ => *for* {e2, ...; en}}
>
> //- *for* {x <- e1; e2; ...; en}/ is translated to
> /e1.flatMap {*case* x => *for* {e2; ... en}}/
>
> Now I rolled up my sleeves and had a short look in Parsers and
> TreeBuilder and I'm considering what may be a dirty workaround.
>
> I'm wondering if it would be possible to make the generator accept
> pattern-less expressions i.e.
>
> Generator ::= Pattern1 (`<-' | '=') Expr [Guard] | Expr [Guard]
>
> and return ValFrom(pos: Position, /pat: Option[Tree]/, rhs: Tree).
>
> I guess that is the hard part but once a pattern-less ValFrom is
> detected it could trigger the Haskell like for-comprehension
> generation in expr0, that is:
>
> case FOR =>
> atPos(in.skipToken()) {
> val (open, close) = if (in.token == LBRACE) (LBRACE, RBRACE)
> else (LPAREN, RPAREN)
> val enums = surround(open, close)(enumerators(), Nil)
> newLinesOpt()
> if (enums.exists(_.pat.isEmpty) {
> makeForLikeHaskell(enums)
> } else if (in.token == YIELD) {
> in.nextToken()
> makeForYield(enums, expr())
> } else {
> makeFor(enums, expr())
> }
>
> I'm not versed in scala compiler nor its plugin mechanism and I'd be
> very thankful if someone would be so kind as to indicate me the right
> way to proceed.
>
> Thanks,
> Sebastien
--
Tony Morris
http://tmorris.net/
Sun, 2010-01-03, 00:07
#3
Re: Support for Haskell like do-comprehensions
As you advance further, you'll find that for-comprehensions are
generally rather clumsy anyway. Have you looked at Control.Applicative?
You might be interested in this:
http://code.google.com/p/scalaz/source/browse/continuous/2010-01-02%2B83...
Sébastien Bocq wrote:
> Well, it is certainly not that bad but the paper here triggered me.
>
> http://lamp.epfl.ch/~rompf/continuations-icfp09.pdf
>
>
> /"Being forced to use for comprehensions everywhere continuations are
> accessed does not make for a pleasant programming style, though."
> /
> If the people behind Scala feel this way towards for comprehensions
> then I'm a bit concerned about the reaction of the ones not familiar
> or averse to FP and I want to reduce the entry barrier as much as
> possible.
>
> Take for instance the concrete example below [1]. I suppose they mean
> that something like this doesn't make for pleasant programming:
>
> for ( _ <- suspendWhile(!join);
> _ <- exec {
> ...
> };
> _ <- Responder.loopWhile(!leave) {
> ...
> for (msg <- getNewMessage;
> _ <- exec {
> ...
> }
> ) yield ()
> }
> )
>
> I can already imagine all the questions that this example will raise
> while with the following code, people with imperative background will
> just feel at home (they won't even miss the semicolon):
>
> for { suspendWhile(!join);
> exec {
> ...
> };
> Responder.loopWhile(!leave) {
> ...
> for {msg <- getNewMessage;
> exec {
> ...
> }
> }
> }
> )
> }
>
> If people here think it is not worth the pain, I'll live with it, I'll
> just go light a candle and pray that people where I work will take it
> as easy as well :-)
>
> [1]
> http://svn2.assembla.com/svn/comet-like_scala/Comet-style%20Web%20Applic...
>
> 2010/1/2 Tony Morris >
>
> Your Haskell code is equivalent to:
>
> do y <- f x
> z <- g y
> return z
>
> It's certainly uglier code, but if we can imagine a Haskell that
> forced
> this, which is effectively what Scala does, would you be that much
> worse
> off?
>
> PS: You don't need the parentheses after the yield keyword
>
> Sébastien Bocq wrote:
> > Hello,
> >
> > I've been playing with Monads in Scala for some time now and I miss
> > the simple syntax of do-comprehensions in Haskell.
> >
> > Consider for instance this code in Haskell:
> >
> > /*do* y <- f x
> > g y
> > /
> > The problem is that the equivalent code in Scala must be written
> like
> > this:
> >
> > /*for* { y <- f(x);
> > z <- g(y) } *yield* (z)
> > /
> > which adds quite some noise compared to the signal.
> >
> > What I'd like is support for expressions like this:
> >
> > /*for* {
> > y <- f(x);
> > g(y)
> > }
> > /
> > which could get translated into:
> >
> > /f(x).flatMap { *case* y => g(y)}/
> >
> > The rules would be similar to Haskell rules, that is:
> >
> > - /*for* {e}/ is translated to/:
> > e
> >
> > /- /*for* {e1; e2; ...; en} /is translated to/
> > e1.flatMap {*case* _ => *for* {e2, ...; en}}
> >
> > //- *for* {x <- e1; e2; ...; en}/ is translated to
> > /e1.flatMap {*case* x => *for* {e2; ... en}}/
> >
> > Now I rolled up my sleeves and had a short look in Parsers and
> > TreeBuilder and I'm considering what may be a dirty workaround.
> >
> > I'm wondering if it would be possible to make the generator accept
> > pattern-less expressions i.e.
> >
> > Generator ::= Pattern1 (`<-' | '=') Expr [Guard] | Expr [Guard]
> >
> > and return ValFrom(pos: Position, /pat: Option[Tree]/, rhs: Tree).
> >
> > I guess that is the hard part but once a pattern-less ValFrom is
> > detected it could trigger the Haskell like for-comprehension
> > generation in expr0, that is:
> >
> > case FOR =>
> > atPos(in.skipToken()) {
> > val (open, close) = if (in.token == LBRACE) (LBRACE,
> RBRACE)
> > else (LPAREN, RPAREN)
> > val enums = surround(open, close)(enumerators(), Nil)
> > newLinesOpt()
> > if (enums.exists(_.pat.isEmpty) {
> > makeForLikeHaskell(enums)
> > } else if (in.token == YIELD) {
> > in.nextToken()
> > makeForYield(enums, expr())
> > } else {
> > makeFor(enums, expr())
> > }
> >
> > I'm not versed in scala compiler nor its plugin mechanism and I'd be
> > very thankful if someone would be so kind as to indicate me the
> right
> > way to proceed.
> >
> > Thanks,
> > Sebastien
>
> --
> Tony Morris
> http://tmorris.net/
>
>
>
Sun, 2010-01-03, 00:37
#4
Re: Support for Haskell like do-comprehensions
It is a beyond my current level but it seems it is not suited for my purpose because I need the bind operator to control application of side effects (c.f. miffy example).
2010/1/2 Tony Morris <tonymorris@gmail.com>
2010/1/2 Tony Morris <tonymorris@gmail.com>
As you advance further, you'll find that for-comprehensions are
generally rather clumsy anyway. Have you looked at Control.Applicative?
You might be interested in this:
http://code.google.com/p/scalaz/source/browse/continuous/2010-01-02%2B8321.4049s/example/scalaz/ExampleApplicative.scala
Sébastien Bocq wrote:
> Well, it is certainly not that bad but the paper here triggered me.
>
> http://lamp.epfl.ch/~rompf/continuations-icfp09.pdf
> <http://lamp.epfl.ch/%7Erompf/continuations-icfp09.pdf>
>
> /"Being forced to use for comprehensions everywhere continuations are
> accessed does not make for a pleasant programming style, though."
> /
> If the people behind Scala feel this way towards for comprehensions
> then I'm a bit concerned about the reaction of the ones not familiar
> or averse to FP and I want to reduce the entry barrier as much as
> possible.
>
> Take for instance the concrete example below [1]. I suppose they mean
> that something like this doesn't make for pleasant programming:
>
> for ( _ <- suspendWhile(!join);
> _ <- exec {
> ...
> };
> _ <- Responder.loopWhile(!leave) {
> ...
> for (msg <- getNewMessage;
> _ <- exec {
> ...
> }
> ) yield ()
> }
> )
>
> I can already imagine all the questions that this example will raise
> while with the following code, people with imperative background will
> just feel at home (they won't even miss the semicolon):
>
> for { suspendWhile(!join);
> exec {
> ...
> };
> Responder.loopWhile(!leave) {
> ...
> for {msg <- getNewMessage;
> exec {
> ...
> }
> }
> }
> )
> }
>
> If people here think it is not worth the pain, I'll live with it, I'll
> just go light a candle and pray that people where I work will take it
> as easy as well :-)
>
> [1]
> http://svn2.assembla.com/svn/comet-like_scala/Comet-style%20Web%20Applications%20in%20Scala.pdf
>
> 2010/1/2 Tony Morris <tonymorris@gmail.com <mailto:tonymorris@gmail.com>>
>
> Your Haskell code is equivalent to:
>
> do y <- f x
> z <- g y
> return z
>
> It's certainly uglier code, but if we can imagine a Haskell that
> forced
> this, which is effectively what Scala does, would you be that much
> worse
> off?
>
> PS: You don't need the parentheses after the yield keyword
>
> Sébastien Bocq wrote:
> > Hello,
> >
> > I've been playing with Monads in Scala for some time now and I miss
> > the simple syntax of do-comprehensions in Haskell.
> >
> > Consider for instance this code in Haskell:
> >
> > /*do* y <- f x
> > g y
> > /
> > The problem is that the equivalent code in Scala must be written
> like
> > this:
> >
> > /*for* { y <- f(x);
> > z <- g(y) } *yield* (z)
> > /
> > which adds quite some noise compared to the signal.
> >
> > What I'd like is support for expressions like this:
> >
> > /*for* {
> > y <- f(x);
> > g(y)
> > }
> > /
> > which could get translated into:
> >
> > /f(x).flatMap { *case* y => g(y)}/
> >
> > The rules would be similar to Haskell rules, that is:
> >
> > - /*for* {e}/ is translated to/:
> > e
> >
> > /- /*for* {e1; e2; ...; en} /is translated to/
> > e1.flatMap {*case* _ => *for* {e2, ...; en}}
> >
> > //- *for* {x <- e1; e2; ...; en}/ is translated to
> > /e1.flatMap {*case* x => *for* {e2; ... en}}/
> >
> > Now I rolled up my sleeves and had a short look in Parsers and
> > TreeBuilder and I'm considering what may be a dirty workaround.
> >
> > I'm wondering if it would be possible to make the generator accept
> > pattern-less expressions i.e.
> >
> > Generator ::= Pattern1 (`<-' | '=') Expr [Guard] | Expr [Guard]
> >
> > and return ValFrom(pos: Position, /pat: Option[Tree]/, rhs: Tree).
> >
> > I guess that is the hard part but once a pattern-less ValFrom is
> > detected it could trigger the Haskell like for-comprehension
> > generation in expr0, that is:
> >
> > case FOR =>
> > atPos(in.skipToken()) {
> > val (open, close) = if (in.token == LBRACE) (LBRACE,
> RBRACE)
> > else (LPAREN, RPAREN)
> > val enums = surround(open, close)(enumerators(), Nil)
> > newLinesOpt()
> > if (enums.exists(_.pat.isEmpty) {
> > makeForLikeHaskell(enums)
> > } else if (in.token == YIELD) {
> > in.nextToken()
> > makeForYield(enums, expr())
> > } else {
> > makeFor(enums, expr())
> > }
> >
> > I'm not versed in scala compiler nor its plugin mechanism and I'd be
> > very thankful if someone would be so kind as to indicate me the
> right
> > way to proceed.
> >
> > Thanks,
> > Sebastien
>
> --
> Tony Morris
> http://tmorris.net/
>
>
>
--
Tony Morris
http://tmorris.net/
Sun, 2010-01-03, 00:47
#5
Re: Support for Haskell like do-comprehensions
While a monad (bind) can "do more", many uses can be generalised to
Applicative, including many for-comprehensions.
for(x <- a; y <- b) yield f(a)(b)
can be written in Applicative style:
b ⊛ (a ∘ f)
The ⊛ function has the signature: F[A] => F[A => B] => F[B]
and the ∘ function is a generalised compose/map: F[A] => (A => B) => F[B]
The reason the above can be written in applicative style is because the
"binding value" x is not used further down the chain (e.g. in the
expression b). If it was, then bind/flatMap/Monad would be a
requirement, but even then, there are often less clumsy solutions than a
for-comprehension (consider lambdabot's @pl and @undo commands when
using do-notation).
I don't think you should dismiss Applicative just yet!
http://www.soi.city.ac.uk/~ross/papers/Applicative.html
Sébastien Bocq wrote:
> It is a beyond my current level but it seems it is not suited for my
> purpose because I need the bind operator to control application of
> side effects (c.f. miffy example).
>
> 2010/1/2 Tony Morris >
>
> As you advance further, you'll find that for-comprehensions are
> generally rather clumsy anyway. Have you looked at
> Control.Applicative?
>
> You might be interested in this:
> http://code.google.com/p/scalaz/source/browse/continuous/2010-01-02%2B83...
>
>
> Sébastien Bocq wrote:
> > Well, it is certainly not that bad but the paper here triggered me.
> >
> > http://lamp.epfl.ch/~rompf/continuations-icfp09.pdf
>
> >
> >
> > /"Being forced to use for comprehensions everywhere
> continuations are
> > accessed does not make for a pleasant programming style, though."
> > /
> > If the people behind Scala feel this way towards for comprehensions
> > then I'm a bit concerned about the reaction of the ones not familiar
> > or averse to FP and I want to reduce the entry barrier as much as
> > possible.
> >
> > Take for instance the concrete example below [1]. I suppose they
> mean
> > that something like this doesn't make for pleasant programming:
> >
> > for ( _ <- suspendWhile(!join);
> > _ <- exec {
> > ...
> > };
> > _ <- Responder.loopWhile(!leave) {
> > ...
> > for (msg <- getNewMessage;
> > _ <- exec {
> > ...
> > }
> > ) yield ()
> > }
> > )
> >
> > I can already imagine all the questions that this example will raise
> > while with the following code, people with imperative background
> will
> > just feel at home (they won't even miss the semicolon):
> >
> > for { suspendWhile(!join);
> > exec {
> > ...
> > };
> > Responder.loopWhile(!leave) {
> > ...
> > for {msg <- getNewMessage;
> > exec {
> > ...
> > }
> > }
> > }
> > )
> > }
> >
> > If people here think it is not worth the pain, I'll live with
> it, I'll
> > just go light a candle and pray that people where I work will
> take it
> > as easy as well :-)
> >
> > [1]
> >
> http://svn2.assembla.com/svn/comet-like_scala/Comet-style%20Web%20Applic...
> >
> > 2010/1/2 Tony Morris >>
> >
> > Your Haskell code is equivalent to:
> >
> > do y <- f x
> > z <- g y
> > return z
> >
> > It's certainly uglier code, but if we can imagine a Haskell that
> > forced
> > this, which is effectively what Scala does, would you be
> that much
> > worse
> > off?
> >
> > PS: You don't need the parentheses after the yield keyword
> >
> > Sébastien Bocq wrote:
> > > Hello,
> > >
> > > I've been playing with Monads in Scala for some time now
> and I miss
> > > the simple syntax of do-comprehensions in Haskell.
> > >
> > > Consider for instance this code in Haskell:
> > >
> > > /*do* y <- f x
> > > g y
> > > /
> > > The problem is that the equivalent code in Scala must be
> written
> > like
> > > this:
> > >
> > > /*for* { y <- f(x);
> > > z <- g(y) } *yield* (z)
> > > /
> > > which adds quite some noise compared to the signal.
> > >
> > > What I'd like is support for expressions like this:
> > >
> > > /*for* {
> > > y <- f(x);
> > > g(y)
> > > }
> > > /
> > > which could get translated into:
> > >
> > > /f(x).flatMap { *case* y => g(y)}/
> > >
> > > The rules would be similar to Haskell rules, that is:
> > >
> > > - /*for* {e}/ is translated to/:
> > > e
> > >
> > > /- /*for* {e1; e2; ...; en} /is translated to/
> > > e1.flatMap {*case* _ => *for* {e2, ...; en}}
> > >
> > > //- *for* {x <- e1; e2; ...; en}/ is translated to
> > > /e1.flatMap {*case* x => *for* {e2; ... en}}/
> > >
> > > Now I rolled up my sleeves and had a short look in Parsers and
> > > TreeBuilder and I'm considering what may be a dirty
> workaround.
> > >
> > > I'm wondering if it would be possible to make the
> generator accept
> > > pattern-less expressions i.e.
> > >
> > > Generator ::= Pattern1 (`<-' | '=') Expr [Guard] | Expr
> [Guard]
> > >
> > > and return ValFrom(pos: Position, /pat: Option[Tree]/,
> rhs: Tree).
> > >
> > > I guess that is the hard part but once a pattern-less
> Val>From is
> > > detected it could trigger the Haskell like for-comprehension
> > > generation in expr0, that is:
> > >
> > > case FOR =>
> > > atPos(in.skipToken()) {
> > > val (open, close) = if (in.token == LBRACE) (LBRACE,
> > RBRACE)
> > > else (LPAREN, RPAREN)
> > > val enums = surround(open, close)(enumerators(),
> Nil)
> > > newLinesOpt()
> > > if (enums.exists(_.pat.isEmpty) {
> > > makeForLikeHaskell(enums)
> > > } else if (in.token == YIELD) {
> > > in.nextToken()
> > > makeForYield(enums, expr())
> > > } else {
> > > makeFor(enums, expr())
> > > }
> > >
> > > I'm not versed in scala compiler nor its plugin mechanism
> and I'd be
> > > very thankful if someone would be so kind as to indicate
> me the
> > right
> > > way to proceed.
> > >
> > > Thanks,
> > > Sebastien
> >
> > --
> > Tony Morris
> > http://tmorris.net/
> >
> >
> >
>
> --
> Tony Morris
> http://tmorris.net/
>
>
>
Sun, 2010-01-03, 01:17
#6
Re: Support for Haskell like do-comprehensions
Thanks for the hint, I'm not dismissing it, I take good note but for the problem at hand I really need the imperative style (c.f. section 5 "Applicative vs. Monad?").
2010/1/3 Tony Morris <tonymorris@gmail.com>
2010/1/3 Tony Morris <tonymorris@gmail.com>
While a monad (bind) can "do more", many uses can be generalised to
Applicative, including many for-comprehensions.
for(x <- a; y <- b) yield f(a)(b)
can be written in Applicative style:
b ⊛ (a ∘ f)
The ⊛ function has the signature: F[A] => F[A => B] => F[B]
and the ∘ function is a generalised compose/map: F[A] => (A => B) => F[B]
The reason the above can be written in applicative style is because the
"binding value" x is not used further down the chain (e.g. in the
expression b). If it was, then bind/flatMap/Monad would be a
requirement, but even then, there are often less clumsy solutions than a
for-comprehension (consider lambdabot's @pl and @undo commands when
using do-notation).
I don't think you should dismiss Applicative just yet!
http://www.soi.city.ac.uk/~ross/papers/Applicative.html
Sébastien Bocq wrote:
> It is a beyond my current level but it seems it is not suited for my
> purpose because I need the bind operator to control application of
> side effects (c.f. miffy example).
>
> 2010/1/2 Tony Morris <tonymorris@gmail.com <mailto:tonymorris@gmail.com>>
>
> As you advance further, you'll find that for-comprehensions are
> generally rather clumsy anyway. Have you looked at
> Control.Applicative?
>
> You might be interested in this:
> http://code.google.com/p/scalaz/source/browse/continuous/2010-01-02%2B8321.4049s/example/scalaz/ExampleApplicative.scala
>
>
> Sébastien Bocq wrote:
> > Well, it is certainly not that bad but the paper here triggered me.
> >
> > http://lamp.epfl.ch/~rompf/continuations-icfp09.pdf
> <http://lamp.epfl.ch/%7Erompf/continuations-icfp09.pdf>
> > <http://lamp.epfl.ch/%7Erompf/continuations-icfp09.pdf>
> >
> > /"Being forced to use for comprehensions everywhere
> continuations are
> > accessed does not make for a pleasant programming style, though."
> > /
> > If the people behind Scala feel this way towards for comprehensions
> > then I'm a bit concerned about the reaction of the ones not familiar
> > or averse to FP and I want to reduce the entry barrier as much as
> > possible.
> >
> > Take for instance the concrete example below [1]. I suppose they
> mean
> > that something like this doesn't make for pleasant programming:
> >
> > for ( _ <- suspendWhile(!join);
> > _ <- exec {
> > ...
> > };
> > _ <- Responder.loopWhile(!leave) {
> > ...
> > for (msg <- getNewMessage;
> > _ <- exec {
> > ...
> > }
> > ) yield ()
> > }
> > )
> >
> > I can already imagine all the questions that this example will raise
> > while with the following code, people with imperative background
> will
> > just feel at home (they won't even miss the semicolon):
> >
> > for { suspendWhile(!join);
> > exec {
> > ...
> > };
> > Responder.loopWhile(!leave) {
> > ...
> > for {msg <- getNewMessage;
> > exec {
> > ...
> > }
> > }
> > }
> > )
> > }
> >
> > If people here think it is not worth the pain, I'll live with
> it, I'll
> > just go light a candle and pray that people where I work will
> take it
> > as easy as well :-)
> >
> > [1]
> >
> http://svn2.assembla.com/svn/comet-like_scala/Comet-style%20Web%20Applications%20in%20Scala.pdf
> >
> > 2010/1/2 Tony Morris <tonymorris@gmail.com
> <mailto:tonymorris@gmail.com> <mailto:tonymorris@gmail.com
> <mailto:tonymorris@gmail.com>>>
> >
> > Your Haskell code is equivalent to:
> >
> > do y <- f x
> > z <- g y
> > return z
> >
> > It's certainly uglier code, but if we can imagine a Haskell that
> > forced
> > this, which is effectively what Scala does, would you be
> that much
> > worse
> > off?
> >
> > PS: You don't need the parentheses after the yield keyword
> >
> > Sébastien Bocq wrote:
> > > Hello,
> > >
> > > I've been playing with Monads in Scala for some time now
> and I miss
> > > the simple syntax of do-comprehensions in Haskell.
> > >
> > > Consider for instance this code in Haskell:
> > >
> > > /*do* y <- f x
> > > g y
> > > /
> > > The problem is that the equivalent code in Scala must be
> written
> > like
> > > this:
> > >
> > > /*for* { y <- f(x);
> > > z <- g(y) } *yield* (z)
> > > /
> > > which adds quite some noise compared to the signal.
> > >
> > > What I'd like is support for expressions like this:
> > >
> > > /*for* {
> > > y <- f(x);
> > > g(y)
> > > }
> > > /
> > > which could get translated into:
> > >
> > > /f(x).flatMap { *case* y => g(y)}/
> > >
> > > The rules would be similar to Haskell rules, that is:
> > >
> > > - /*for* {e}/ is translated to/:
> > > e
> > >
> > > /- /*for* {e1; e2; ...; en} /is translated to/
> > > e1.flatMap {*case* _ => *for* {e2, ...; en}}
> > >
> > > //- *for* {x <- e1; e2; ...; en}/ is translated to
> > > /e1.flatMap {*case* x => *for* {e2; ... en}}/
> > >
> > > Now I rolled up my sleeves and had a short look in Parsers and
> > > TreeBuilder and I'm considering what may be a dirty
> workaround.
> > >
> > > I'm wondering if it would be possible to make the
> generator accept
> > > pattern-less expressions i.e.
> > >
> > > Generator ::= Pattern1 (`<-' | '=') Expr [Guard] | Expr
> [Guard]
> > >
> > > and return ValFrom(pos: Position, /pat: Option[Tree]/,
> rhs: Tree).
> > >
> > > I guess that is the hard part but once a pattern-less
> Val>From is
> > > detected it could trigger the Haskell like for-comprehension
> > > generation in expr0, that is:
> > >
> > > case FOR =>
> > > atPos(in.skipToken()) {
> > > val (open, close) = if (in.token == LBRACE) (LBRACE,
> > RBRACE)
> > > else (LPAREN, RPAREN)
> > > val enums = surround(open, close)(enumerators(),
> Nil)
> > > newLinesOpt()
> > > if (enums.exists(_.pat.isEmpty) {
> > > makeForLikeHaskell(enums)
> > > } else if (in.token == YIELD) {
> > > in.nextToken()
> > > makeForYield(enums, expr())
> > > } else {
> > > makeFor(enums, expr())
> > > }
> > >
> > > I'm not versed in scala compiler nor its plugin mechanism
> and I'd be
> > > very thankful if someone would be so kind as to indicate
> me the
> > right
> > > way to proceed.
> > >
> > > Thanks,
> > > Sebastien
> >
> > --
> > Tony Morris
> > http://tmorris.net/
> >
> >
> >
>
> --
> Tony Morris
> http://tmorris.net/
>
>
>
--
Tony Morris
http://tmorris.net/
Sun, 2010-01-03, 01:27
#7
Re: Support for Haskell like do-comprehensions
By needing the imperative style, do you mean syntax?
You can do imperative programming in Applicative after all. Try this:
Prelude Control.Applicative> (++) <$> readFile "/etc/passwd" <*>
readFile "/etc/group"
Sébastien Bocq wrote:
> Thanks for the hint, I'm not dismissing it, I take good note but for
> the problem at hand I really need the imperative style (c.f. section 5
> "Applicative vs. Monad?").
>
> 2010/1/3 Tony Morris >
>
> While a monad (bind) can "do more", many uses can be generalised to
> Applicative, including many for-comprehensions.
>
> for(x <- a; y <- b) yield f(a)(b)
>
> can be written in Applicative style:
>
> b ⊛ (a ∘ f)
>
> The ⊛ function has the signature: F[A] => F[A => B] => F[B]
> and the ∘ function is a generalised compose/map: F[A] => (A => B)
> => F[B]
>
> The reason the above can be written in applicative style is
> because the
> "binding value" x is not used further down the chain (e.g. in the
> expression b). If it was, then bind/flatMap/Monad would be a
> requirement, but even then, there are often less clumsy solutions
> than a
> for-comprehension (consider lambdabot's @pl and @undo commands when
> using do-notation).
>
> I don't think you should dismiss Applicative just yet!
> http://www.soi.city.ac.uk/~ross/papers/Applicative.html
>
>
> Sébastien Bocq wrote:
> > It is a beyond my current level but it seems it is not suited for my
> > purpose because I need the bind operator to control application of
> > side effects (c.f. miffy example).
> >
> > 2010/1/2 Tony Morris >>
> >
> > As you advance further, you'll find that for-comprehensions are
> > generally rather clumsy anyway. Have you looked at
> > Control.Applicative?
> >
> > You might be interested in this:
> >
> http://code.google.com/p/scalaz/source/browse/continuous/2010-01-02%2B83...
> >
> >
> > Sébastien Bocq wrote:
> > > Well, it is certainly not that bad but the paper here
> triggered me.
> > >
> > > http://lamp.epfl.ch/~rompf/continuations-icfp09.pdf
>
> >
> > >
> > >
> > > /"Being forced to use for comprehensions everywhere
> > continuations are
> > > accessed does not make for a pleasant programming style,
> though."
> > > /
> > > If the people behind Scala feel this way towards for
> comprehensions
> > > then I'm a bit concerned about the reaction of the ones
> not familiar
> > > or averse to FP and I want to reduce the entry barrier as
> much as
> > > possible.
> > >
> > > Take for instance the concrete example below [1]. I
> suppose they
> > mean
> > > that something like this doesn't make for pleasant
> programming:
> > >
> > > for ( _ <- suspendWhile(!join);
> > > _ <- exec {
> > > ...
> > > };
> > > _ <- Responder.loopWhile(!leave) {
> > > ...
> > > for (msg <- getNewMessage;
> > > _ <- exec {
> > > ...
> > > }
> > > ) yield ()
> > > }
> > > )
> > >
> > > I can already imagine all the questions that this example
> will raise
> > > while with the following code, people with imperative
> background
> > will
> > > just feel at home (they won't even miss the semicolon):
> > >
> > > for { suspendWhile(!join);
> > > exec {
> > > ...
> > > };
> > > Responder.loopWhile(!leave) {
> > > ...
> > > for {msg <- getNewMessage;
> > > exec {
> > > ...
> > > }
> > > }
> > > }
> > > )
> > > }
> > >
> > > If people here think it is not worth the pain, I'll live with
> > it, I'll
> > > just go light a candle and pray that people where I work will
> > take it
> > > as easy as well :-)
> > >
> > > [1]
> > >
> >
> http://svn2.assembla.com/svn/comet-like_scala/Comet-style%20Web%20Applic...
> > >
> > > 2010/1/2 Tony Morris
> > >
>
> > >>>
> > >
> > > Your Haskell code is equivalent to:
> > >
> > > do y <- f x
> > > z <- g y
> > > return z
> > >
> > > It's certainly uglier code, but if we can imagine a
> Haskell that
> > > forced
> > > this, which is effectively what Scala does, would you be
> > that much
> > > worse
> > > off?
> > >
> > > PS: You don't need the parentheses after the yield keyword
> > >
> > > Sébastien Bocq wrote:
> > > > Hello,
> > > >
> > > > I've been playing with Monads in Scala for some time now
> > and I miss
> > > > the simple syntax of do-comprehensions in Haskell.
> > > >
> > > > Consider for instance this code in Haskell:
> > > >
> > > > /*do* y <- f x
> > > > g y
> > > > /
> > > > The problem is that the equivalent code in Scala must be
> > written
> > > like
> > > > this:
> > > >
> > > > /*for* { y <- f(x);
> > > > z <- g(y) } *yield* (z)
> > > > /
> > > > which adds quite some noise compared to the signal.
> > > >
> > > > What I'd like is support for expressions like this:
> > > >
> > > > /*for* {
> > > > y <- f(x);
> > > > g(y)
> > > > }
> > > > /
> > > > which could get translated into:
> > > >
> > > > /f(x).flatMap { *case* y => g(y)}/
> > > >
> > > > The rules would be similar to Haskell rules, that is:
> > > >
> > > > - /*for* {e}/ is translated to/:
> > > > e
> > > >
> > > > /- /*for* {e1; e2; ...; en} /is translated to/
> > > > e1.flatMap {*case* _ => *for* {e2, ...; en}}
> > > >
> > > > //- *for* {x <- e1; e2; ...; en}/ is translated to
> > > > /e1.flatMap {*case* x => *for* {e2; ... en}}/
> > > >
> > > > Now I rolled up my sleeves and had a short look in
> Parsers and
> > > > TreeBuilder and I'm considering what may be a dirty
> > workaround.
> > > >
> > > > I'm wondering if it would be possible to make the
> > generator accept
> > > > pattern-less expressions i.e.
> > > >
> > > > Generator ::= Pattern1 (`<-' | '=') Expr [Guard] | Expr
> > [Guard]
> > > >
> > > > and return ValFrom(pos: Position, /pat: Option[Tree]/,
> > rhs: Tree).
> > > >
> > > > I guess that is the hard part but once a pattern-less
> > Val>From is
> > > > detected it could trigger the Haskell like
> for-comprehension
> > > > generation in expr0, that is:
> > > >
> > > > case FOR =>
> > > > atPos(in.skipToken()) {
> > > > val (open, close) = if (in.token ==
> LBRACE) (LBRACE,
> > > RBRACE)
> > > > else (LPAREN, RPAREN)
> > > > val enums = surround(open,
> close)(enumerators(),
> > Nil)
> > > > newLinesOpt()
> > > > if (enums.exists(_.pat.isEmpty) {
> > > > makeForLikeHaskell(enums)
> > > > } else if (in.token == YIELD) {
> > > > in.nextToken()
> > > > makeForYield(enums, expr())
> > > > } else {
> > > > makeFor(enums, expr())
> > > > }
> > > >
> > > > I'm not versed in scala compiler nor its plugin
> mechanism
> > and I'd be
> > > > very thankful if someone would be so kind as to indicate
> > me the
> > > right
> > > > way to proceed.
> > > >
> > > > Thanks,
> > > > Sebastien
> > >
> > > --
> > > Tony Morris
> > > http://tmorris.net/
> > >
> > >
> > >
> >
> > --
> > Tony Morris
> > http://tmorris.net/
> >
> >
> >
>
> --
> Tony Morris
> http://tmorris.net/
>
>
>
Sun, 2010-01-03, 02:17
#8
Re: Support for Haskell like do-comprehensions
Yes because I want to control the application of side effects from the result of previous side effects but your example is nice and corresponds to:
miffy [[ <= getSpeed getSpeedLimit]] stepOnIt checkMirror
which is indeed less awkward than relying only on for comprehensions. I think I get it.
So instead of:
for (a <- getSpeed;
b <- getSpeedLimit;
_ <- if (a <= b) stepOnIt else checkMirror) yield ()
How does the alternative looks like in scala using Applicative (and a regular keyboard)?
2010/1/3 Tony Morris <tonymorris@gmail.com>
miffy [[ <= getSpeed getSpeedLimit]] stepOnIt checkMirror
which is indeed less awkward than relying only on for comprehensions. I think I get it.
So instead of:
for (a <- getSpeed;
b <- getSpeedLimit;
_ <- if (a <= b) stepOnIt else checkMirror) yield ()
How does the alternative looks like in scala using Applicative (and a regular keyboard)?
2010/1/3 Tony Morris <tonymorris@gmail.com>
By needing the imperative style, do you mean syntax?
You can do imperative programming in Applicative after all. Try this:
Prelude Control.Applicative> (++) <$> readFile "/etc/passwd" <*>
readFile "/etc/group"
Sébastien Bocq wrote:
> Thanks for the hint, I'm not dismissing it, I take good note but for
> the problem at hand I really need the imperative style (c.f. section 5
> "Applicative vs. Monad?").
>
> 2010/1/3 Tony Morris <tonymorris@gmail.com <mailto:tonymorris@gmail.com>>
>
> While a monad (bind) can "do more", many uses can be generalised to
> Applicative, including many for-comprehensions.
>
> for(x <- a; y <- b) yield f(a)(b)
>
> can be written in Applicative style:
>
> b ⊛ (a ∘ f)
>
> The ⊛ function has the signature: F[A] => F[A => B] => F[B]
> and the ∘ function is a generalised compose/map: F[A] => (A => B)
> => F[B]
>
> The reason the above can be written in applicative style is
> because the
> "binding value" x is not used further down the chain (e.g. in the
> expression b). If it was, then bind/flatMap/Monad would be a
> requirement, but even then, there are often less clumsy solutions
> than a
> for-comprehension (consider lambdabot's @pl and @undo commands when
> using do-notation).
>
> I don't think you should dismiss Applicative just yet!
> http://www.soi.city.ac.uk/~ross/papers/Applicative.html
> <http://www.soi.city.ac.uk/%7Eross/papers/Applicative.html>
>
> Sébastien Bocq wrote:
> > It is a beyond my current level but it seems it is not suited for my
> > purpose because I need the bind operator to control application of
> > side effects (c.f. miffy example).
> >
> > 2010/1/2 Tony Morris <tonymorris@gmail.com
> <mailto:tonymorris@gmail.com> <mailto:tonymorris@gmail.com
> <mailto:tonymorris@gmail.com>>>
> >
> > As you advance further, you'll find that for-comprehensions are
> > generally rather clumsy anyway. Have you looked at
> > Control.Applicative?
> >
> > You might be interested in this:
> >
> http://code.google.com/p/scalaz/source/browse/continuous/2010-01-02%2B8321.4049s/example/scalaz/ExampleApplicative.scala
> >
> >
> > Sébastien Bocq wrote:
> > > Well, it is certainly not that bad but the paper here
> triggered me.
> > >
> > > http://lamp.epfl.ch/~rompf/continuations-icfp09.pdf
> <http://lamp.epfl.ch/%7Erompf/continuations-icfp09.pdf>
> > <http://lamp.epfl.ch/%7Erompf/continuations-icfp09.pdf>
> > > <http://lamp.epfl.ch/%7Erompf/continuations-icfp09.pdf>
> > >
> > > /"Being forced to use for comprehensions everywhere
> > continuations are
> > > accessed does not make for a pleasant programming style,
> though."
> > > /
> > > If the people behind Scala feel this way towards for
> comprehensions
> > > then I'm a bit concerned about the reaction of the ones
> not familiar
> > > or averse to FP and I want to reduce the entry barrier as
> much as
> > > possible.
> > >
> > > Take for instance the concrete example below [1]. I
> suppose they
> > mean
> > > that something like this doesn't make for pleasant
> programming:
> > >
> > > for ( _ <- suspendWhile(!join);
> > > _ <- exec {
> > > ...
> > > };
> > > _ <- Responder.loopWhile(!leave) {
> > > ...
> > > for (msg <- getNewMessage;
> > > _ <- exec {
> > > ...
> > > }
> > > ) yield ()
> > > }
> > > )
> > >
> > > I can already imagine all the questions that this example
> will raise
> > > while with the following code, people with imperative
> background
> > will
> > > just feel at home (they won't even miss the semicolon):
> > >
> > > for { suspendWhile(!join);
> > > exec {
> > > ...
> > > };
> > > Responder.loopWhile(!leave) {
> > > ...
> > > for {msg <- getNewMessage;
> > > exec {
> > > ...
> > > }
> > > }
> > > }
> > > )
> > > }
> > >
> > > If people here think it is not worth the pain, I'll live with
> > it, I'll
> > > just go light a candle and pray that people where I work will
> > take it
> > > as easy as well :-)
> > >
> > > [1]
> > >
> >
> http://svn2.assembla.com/svn/comet-like_scala/Comet-style%20Web%20Applications%20in%20Scala.pdf
> > >
> > > 2010/1/2 Tony Morris <tonymorris@gmail.com
> <mailto:tonymorris@gmail.com>
> > <mailto:tonymorris@gmail.com <mailto:tonymorris@gmail.com>>
> <mailto:tonymorris@gmail.com <mailto:tonymorris@gmail.com>
> > <mailto:tonymorris@gmail.com <mailto:tonymorris@gmail.com>>>>
> > >
> > > Your Haskell code is equivalent to:
> > >
> > > do y <- f x
> > > z <- g y
> > > return z
> > >
> > > It's certainly uglier code, but if we can imagine a
> Haskell that
> > > forced
> > > this, which is effectively what Scala does, would you be
> > that much
> > > worse
> > > off?
> > >
> > > PS: You don't need the parentheses after the yield keyword
> > >
> > > Sébastien Bocq wrote:
> > > > Hello,
> > > >
> > > > I've been playing with Monads in Scala for some time now
> > and I miss
> > > > the simple syntax of do-comprehensions in Haskell.
> > > >
> > > > Consider for instance this code in Haskell:
> > > >
> > > > /*do* y <- f x
> > > > g y
> > > > /
> > > > The problem is that the equivalent code in Scala must be
> > written
> > > like
> > > > this:
> > > >
> > > > /*for* { y <- f(x);
> > > > z <- g(y) } *yield* (z)
> > > > /
> > > > which adds quite some noise compared to the signal.
> > > >
> > > > What I'd like is support for expressions like this:
> > > >
> > > > /*for* {
> > > > y <- f(x);
> > > > g(y)
> > > > }
> > > > /
> > > > which could get translated into:
> > > >
> > > > /f(x).flatMap { *case* y => g(y)}/
> > > >
> > > > The rules would be similar to Haskell rules, that is:
> > > >
> > > > - /*for* {e}/ is translated to/:
> > > > e
> > > >
> > > > /- /*for* {e1; e2; ...; en} /is translated to/
> > > > e1.flatMap {*case* _ => *for* {e2, ...; en}}
> > > >
> > > > //- *for* {x <- e1; e2; ...; en}/ is translated to
> > > > /e1.flatMap {*case* x => *for* {e2; ... en}}/
> > > >
> > > > Now I rolled up my sleeves and had a short look in
> Parsers and
> > > > TreeBuilder and I'm considering what may be a dirty
> > workaround.
> > > >
> > > > I'm wondering if it would be possible to make the
> > generator accept
> > > > pattern-less expressions i.e.
> > > >
> > > > Generator ::= Pattern1 (`<-' | '=') Expr [Guard] | Expr
> > [Guard]
> > > >
> > > > and return ValFrom(pos: Position, /pat: Option[Tree]/,
> > rhs: Tree).
> > > >
> > > > I guess that is the hard part but once a pattern-less
> > Val>From is
> > > > detected it could trigger the Haskell like
> for-comprehension
> > > > generation in expr0, that is:
> > > >
> > > > case FOR =>
> > > > atPos(in.skipToken()) {
> > > > val (open, close) = if (in.token ==
> LBRACE) (LBRACE,
> > > RBRACE)
> > > > else (LPAREN, RPAREN)
> > > > val enums = surround(open,
> close)(enumerators(),
> > Nil)
> > > > newLinesOpt()
> > > > if (enums.exists(_.pat.isEmpty) {
> > > > makeForLikeHaskell(enums)
> > > > } else if (in.token == YIELD) {
> > > > in.nextToken()
> > > > makeForYield(enums, expr())
> > > > } else {
> > > > makeFor(enums, expr())
> > > > }
> > > >
> > > > I'm not versed in scala compiler nor its plugin
> mechanism
> > and I'd be
> > > > very thankful if someone would be so kind as to indicate
> > me the
> > > right
> > > > way to proceed.
> > > >
> > > > Thanks,
> > > > Sebastien
> > >
> > > --
> > > Tony Morris
> > > http://tmorris.net/
> > >
> > >
> > >
> >
> > --
> > Tony Morris
> > http://tmorris.net/
> >
> >
> >
>
> --
> Tony Morris
> http://tmorris.net/
>
>
>
--
Tony Morris
http://tmorris.net/
Thu, 2010-01-07, 19:17
#9
Re: Support for Haskell like do-comprehensions
What is written in the paper about for-comprehensions is true for do-notation as well. Using continuations, programmers will be able to express the code you are referring to like this:
suspendWhile(!join) ... loopWhile(!leave) { val msg = getNewMessage ... }
which is free of any syntactic noise and arguably much clearer.
If you're after monads, have a look at Section 4.2 in the paper to see how they can be used with continuations. For example,
for { x <- listA y <- listB } yield (x, y)
can be written as
reset { List(listA.reflect, listB.reflect) }
- Tiark
On 02.01.2010, at 23:47, Sébastien Bocq wrote:
suspendWhile(!join) ... loopWhile(!leave) { val msg = getNewMessage ... }
which is free of any syntactic noise and arguably much clearer.
If you're after monads, have a look at Section 4.2 in the paper to see how they can be used with continuations. For example,
for { x <- listA y <- listB } yield (x, y)
can be written as
reset { List(listA.reflect, listB.reflect) }
- Tiark
On 02.01.2010, at 23:47, Sébastien Bocq wrote:
Well, it is certainly not that bad but the paper here triggered me.
http://lamp.epfl.ch/~rompf/continuations-icfp09.pdf
"Being forced to use for comprehensions everywhere continuations are accessed does not make for a pleasant programming style, though."
If the people behind Scala feel this way towards for comprehensions then I'm a bit concerned about the reaction of the ones not familiar or averse to FP and I want to reduce the entry barrier as much as possible.
Take for instance the concrete example below [1]. I suppose they mean that something like this doesn't make for pleasant programming:
for ( _ <- suspendWhile(!join);
_ <- exec {
...
};
_ <- Responder.loopWhile(!leave) {
...
for (msg <- getNewMessage;
_ <- exec {
...
}
) yield ()
}
)
I can already imagine all the questions that this example will raise while with the following code, people with imperative background will just feel at home (they won't even miss the semicolon):
for { suspendWhile(!join);
exec {
...
};
Responder.loopWhile(!leave) {
...
for {msg <- getNewMessage;
exec {
...
}
}
}
)
}
If people here think it is not worth the pain, I'll live with it, I'll just go light a candle and pray that people where I work will take it as easy as well :-)
[1] http://svn2.assembla.com/svn/comet-like_scala/Comet-style%20Web%20Applications%20in%20Scala.pdf
2010/1/2 Tony Morris <tonymorris@gmail.com>Your Haskell code is equivalent to:
do y <- f x
z <- g y
return z
It's certainly uglier code, but if we can imagine a Haskell that forced
this, which is effectively what Scala does, would you be that much worse
off?
PS: You don't need the parentheses after the yield keyword
Sébastien Bocq wrote:
> Hello,
>
> I've been playing with Monads in Scala for some time now and I miss
> the simple syntax of do-comprehensions in Haskell.
>
> Consider for instance this code in Haskell:
>
> /*do* y <- f x
> g y
> /
> The problem is that the equivalent code in Scala must be written like
> this:
>
> /*for* { y <- f(x);
> z <- g(y) } *yield* (z)
> /
> which adds quite some noise compared to the signal.
>
> What I'd like is support for expressions like this:
>
> /*for* {
> y <- f(x);
> g(y)
> }
> /
> which could get translated into:
>
> /f(x).flatMap { *case* y => g(y)}/
>
> The rules would be similar to Haskell rules, that is:
>
> - /*for* {e}/ is translated to/:
> e
>
> /- /*for* {e1; e2; ...; en} /is translated to/
> e1.flatMap {*case* _ => *for* {e2, ...; en}}
>
> //- *for* {x <- e1; e2; ...; en}/ is translated to
> /e1.flatMap {*case* x => *for* {e2; ... en}}/
>
> Now I rolled up my sleeves and had a short look in Parsers and
> TreeBuilder and I'm considering what may be a dirty workaround.
>
> I'm wondering if it would be possible to make the generator accept
> pattern-less expressions i.e.
>
> Generator ::= Pattern1 (`<-' | '=') Expr [Guard] | Expr [Guard]
>
> and return ValFrom(pos: Position, /pat: Option[Tree]/, rhs: Tree).
>
> I guess that is the hard part but once a pattern-less ValFrom is
> detected it could trigger the Haskell like for-comprehension
> generation in expr0, that is:
>
> case FOR =>
> atPos(in.skipToken()) {
> val (open, close) = if (in.token == LBRACE) (LBRACE, RBRACE)
> else (LPAREN, RPAREN)
> val enums = surround(open, close)(enumerators(), Nil)
> newLinesOpt()
> if (enums.exists(_.pat.isEmpty) {
> makeForLikeHaskell(enums)
> } else if (in.token == YIELD) {
> in.nextToken()
> makeForYield(enums, expr())
> } else {
> makeFor(enums, expr())
> }
>
> I'm not versed in scala compiler nor its plugin mechanism and I'd be
> very thankful if someone would be so kind as to indicate me the right
> way to proceed.
>
> Thanks,
> Sebastien
--
Tony Morris
http://tmorris.net/
Thu, 2010-01-14, 00:47
#10
Re: Support for Haskell like do-comprehensions
2010/1/7 Tiark Rompf <tiark.rompf@epfl.ch>
What is written in the paper about for-comprehensions is true for do-notation as well. Using continuations, programmers will be able to express the code you are referring to like this:
suspendWhile(!join) ... loopWhile(!leave) { val msg = getNewMessage ... }
which is free of any syntactic noise and arguably much clearer.
If you're after monads, have a look at Section 4.2 in the paper to see how they can be used with continuations. For example,
for { x <- listA y <- listB } yield (x, y)
can be written as
reset { List(listA.reflect, listB.reflect) }
Monads and for-comprehensions represent already some learning curve for people with no knowledge of FP but I'm afraid delimited continuations with CPS transforms or direct-style monads go well beyond what people may tolerate in regular production environments. Another advantage I see in plain Monad based implementations is that it is a knowledge transferable to/from Haskell. It may be just a small usability problem but from my point of view, if it is not too difficult to add support for it in the compiler, a more convenient syntax for-comprehensions is less disruptive than the alternatives and may be the small thing that convince programmers with imperative background to take the plunge into FP.
Sebastien
Your Haskell code is equivalent to:
do y <- f x
z <- g y
return z
It's certainly uglier code, but if we can imagine a Haskell that forced
this, which is effectively what Scala does, would you be that much worse
off?
PS: You don't need the parentheses after the yield keyword
Sébastien Bocq wrote:
> Hello,
>
> I've been playing with Monads in Scala for some time now and I miss
> the simple syntax of do-comprehensions in Haskell.
>
> Consider for instance this code in Haskell:
>
> /*do* y <- f x
> g y
> /
> The problem is that the equivalent code in Scala must be written like
> this:
>
> /*for* { y <- f(x);
> z <- g(y) } *yield* (z)
> /
> which adds quite some noise compared to the signal.
>
> What I'd like is support for expressions like this:
>
> /*for* {
> y <- f(x);
> g(y)
> }
> /
> which could get translated into:
>
> /f(x).flatMap { *case* y => g(y)}/
>
> The rules would be similar to Haskell rules, that is:
>
> - /*for* {e}/ is translated to/:
> e
>
> /- /*for* {e1; e2; ...; en} /is translated to/
> e1.flatMap {*case* _ => *for* {e2, ...; en}}
>
> //- *for* {x <- e1; e2; ...; en}/ is translated to
> /e1.flatMap {*case* x => *for* {e2; ... en}}/
>
> Now I rolled up my sleeves and had a short look in Parsers and
> TreeBuilder and I'm considering what may be a dirty workaround.
>
> I'm wondering if it would be possible to make the generator accept
> pattern-less expressions i.e.
>
> Generator ::= Pattern1 (`<-' | '=') Expr [Guard] | Expr [Guard]
>
> and return ValFrom(pos: Position, /pat: Option[Tree]/, rhs: Tree).
>
> I guess that is the hard part but once a pattern-less ValFrom is
> detected it could trigger the Haskell like for-comprehension
> generation in expr0, that is:
>
> case FOR =>
> atPos(in.skipToken()) {
> val (open, close) = if (in.token == LBRACE) (LBRACE, RBRACE)
> else (LPAREN, RPAREN)
> val enums = surround(open, close)(enumerators(), Nil)
> newLinesOpt()
> if (enums.exists(_.pat.isEmpty) {
> makeForLikeHaskell(enums)
> } else if (in.token == YIELD) {
> in.nextToken()
> makeForYield(enums, expr())
> } else {
> makeFor(enums, expr())
> }
>
> I'm not versed in scala compiler nor its plugin mechanism and I'd be
> very thankful if someone would be so kind as to indicate me the right
> way to proceed.
>
> Thanks,
> Sebastien