The code & sample apps can be found on Github
Forget the buzz title, this project is still very draft but it’s time to expel it out of my R&D sandbox as imperfect as it might be… Before I lose my sanity while wandering in Scala macro hygiene ;)
What?
Daemonad
is a nasty Scala macro that aims at:
- marking where you manipulate monads or stacks of monads
- compile-checking monadic behavior & implicit monad instances
- allowing to
snoop
monad values deep into (some) monad stacks in the same way asScalaAsync
i.e. in a pseudo-imperative way.
This project is NOT yet stable, NOT very robust so use it at your own risks but we can discuss about it…
Here is what you can write right now.
1 2 3 4 5 6 7 8 9 |
|
Motivations
1 - Experiment writing a very ugly Scala macro
I wanted to write a huge & complex Scala macro that would move pieces of code, create more code, types etc…
I wanted to know the difficulties that it implies.
I felt reckless, courageous!
Nothing could stop me!!!!
Result: I was quite insane and I will certainly write a post-mortem article about it to show the horrible difficulties I’ve encountered. My advice: let people like hit their head against the wall and wait for improved hygienic macros that should come progressively before writing big macros ;)
2 - Investigate ScalaAsync generalization to all monads + (some) monad stacks
I had investigated ScalaAsync code and thought it would be possible to generalize it to all kinds of monads and go further by managing monad stacks.
Result : Simple monads are easy to manage (as seen also in scala-workflow which I discovered very recently) and some monad stacks can be managed with Scalaz monad transformers.
But don’t think you can use all kinds of monad transformers: the limits of Scala compiler with type-lambdas in macros and my very own limits blocked me from going as far as I expected.
So for now, it can manage Future/Option/List stacks & also Either \/ using type aliases.
3 - Explicitly Mark monadic blocks
There are 2 ways of seeing monads:
You don’t need or you don’t want to know what is a monad…
… And yet you use it everyday/everywhere.
This is what most of us (and it’s so shameful) do using those cool map/flatMap
functions provided by Scala libraries that allow to access the values inside Future
, List
, Option
in a protected way etc…
That’s enough for you need in your everyday life, right?
You want to know or you know what is a monad …
… and you want to use them on purpose.
This is what hippy developers do in advanced Scala using Scalaz or even crazier in pure FP languages like Haskell.
Guess what I prefer?
Here is the kind of code I’d like to write :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
Ok I speak about pure functional programming and then about snooping the value out of the monad. This might seem a bit useless or even stupid compared to using directly Monad facilities. I agree and I still wonder about the sanity of this project but I’m stubborn and I try to finish what I start ;)
Back to code Sample
1 2 3 4 5 6 7 8 9 |
|
What does it do ?
monadic
marks the monadic blockmonadic[Future, Option]
declares that you manipulate a stackFuture[Option]
(and no other)snoopX
means that you want to snoop the monad value at X-th level (1, 2, 3, 4 and no more for now)- the macro checks for implicit instances of monads (here
List
,Option
,Future
) and monad transformers (hereOptionT
&ListT
) for this stack - the macro translates this code into crazy embedded
Monad.bind/point/lift/run
… snoop2
is used in first position: if you have usedsnoop1
, the macro would have rejected your monadic block. It’s logical, when you useflatMap
, you always start with the deeper stack of monad and I chose not to change the order of your code as I find this macro is already far too intrusive :)
I’m sure you don’t want to see the code you would have to write for this, this is quite long and boring.
Let just say that this code is generated by a Scala macro for you.
The current generated code isn’t optimized at all and quite redundant but this is for next iterations.
What is working ?
- stacks with List/Option/Either up to depth 4
- custom Monads
- a few preliminary checkings that prevent you from writing stupid code but not so much
- if/then/else & case/match in some cases
What isn’t working ?
- monadic block assigned to a val not explicitly typed.
- many things or edge-cases with mixed monad depth and if/match.
- can’t use advanced monad transformers like StateT or WriterT in monadic block because Scala compiler doesn’t allow what I expected with type lambdas. This needs to be studied further.
A very stupid example to finish with 4-depth stack
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Note that:
- you have to use a type alias to Scalaz \/ to one parametric type.
- you have to help a bit the compiler about type alias S or it will infer \/[A, B] which is not what we want for the monad. This might seem tedious but I’ll study if I can go around this.
- look at the result: you have a 2 elements list and at the end, you have a 8 elements list: WHAT???? No it’s normal, this is the result
flatmap
between first and second and third list. 2*2*2 = 8… nothing strange but it can be surprising at first glance ;)
TODO
- refactor all code because it’s ugly, not robust and redundant!!!
- rely on
MonadTrans[F[_], _]
instead of hardcoding monad transformers as now. - accept custom
MonadTrans
provided in the user code. - steal some inspiration from scala-workflow because I find this code cool.
Special Thanks
- Eugene Burmako (@xeno_by) for helping me each time I was lost in macros
- Jason Zaugg (@retronym) for Scala Async and splicer
- Daniel James (@dwhjames) for the
snoop
name - Thibaut Duplessis (@ornicar) for the monad stack idea
Have a look at the code on Github.
Have snoop22(macrofun)!