Open debasishg opened 8 years ago
@debasishg check that if you missed my tweet
https://github.com/ProjectSeptemberInc/freek/blob/master/src/test/scala/AppSpec.scala#L668-L731
@mandubian - Thanks for the fix ..
One thing I noticed is if I remove the object Bar
here (https://github.com/ProjectSeptemberInc/freek/blob/master/src/test/scala/AppSpec.scala#L708) (it's not being used), and change type PRG = Foo :|: Log.DSL :|: Bar.PRG :||: Repo.PRG
to type PRG = Foo :|: Log.DSL :|: Bar :||: Repo.PRG
in object PRG
at https://github.com/ProjectSeptemberInc/freek/blob/master/src/test/scala/AppSpec.scala#L714, then the unification fails in https://github.com/ProjectSeptemberInc/freek/blob/master/src/test/scala/AppSpec.scala#L721. Possibly related to the types that :||:
takes ..
One more thought - if managing both :|:
and :||:
becomes difficult, another option may be to allow the user write the free monad Free[A]
in the local program (like the first example that I gave).
And in the larger program have a combinator that lifts each individual Free[_]
into the higher kinded coproduct like PRG
. This way we don't abstract the Free
but allow nicer modularity - local programs compose locally, in the global context lift into the coproduct and execute using interpreter composition.
Just a thought though ..
@debasishg actually, I'd like, if possible, to avoid writing Free in local programs if not required because DSL are often simple, auto-sufficient & don't need to be combined like the update case. But if you need it, you can do it too... I'll re-think about it...
Yet, in our case, the solution is quite simple actually:
type PRG = Foo :|: Log.DSL :|: Bar :||: Repo.PRG
simply doesn't compile because :||:
require 1 non-fxnil FX on the left and 1 FX on the right ... In this case, if you use:
type PRG = Foo :|: Log.DSL :|: Bar :|: Repo.PRG
, it works...
Let do the analogy with List:
:|:
is like +:
: DSL :|: FX
<=> a :+ list
:||:
is like ++
: FX1 :||: FX2
<=> list1 ++ list2
In previous versions of freek, I had tried to get rid of :||:
operator but we need to be able to merge 2 FX as your sample has shown.
Does it seem understandable or is it too fuzzy?
because
:||:
require 1 non-fxnil FX on the left and 1 FX on the right
Yes, I got that part. Was just thinking that we may have to introduce additional types like the unused object Bar
in the example above. Otherwise it looks good ..
Actually, the Bar object was just to have 2 programs to combine because this is the case we had and to be able to write Bar.PRG
which is quite explicit.
My reluctance against cake pattern make me tend to put query/store/delete/update helpers in an object Repo
instead of a layer... It reduces the burden on scalac but it reduces a bit the modularity aspect... compromise :D
BTW, we should test the case when we have a local program that itself combines sub-programs to see how Scalac behaves...
@debasishg if you check FX
code & specially :||:
you'll see that it's a pure type trick... :||:
doesn't even provide Head/Tail
sub-types of FXCons
... :||:
is a pure synthetic type just used as a ground for implicit inferences... quite interesting actually ;)
Actually, the Bar object was just to have 2 programs to combine because this is the case we had and to be able to write
Bar.PRG
which is quite explicit.
👍
I plan to take a deeper look at the implementation to understand the type wizardry .. nice stuff in there :-)
Yes don't hesitate ;) Need to improve code IMHO & limit as much as possible the number of implicit resolutions to limit the impact on compile time but I feel like it becomes really usable as is and doesn't make anything weird: it's just helpers to build types combining types but at the end, it's just a Free... The other work field is the type errors: i try to make the code generate errors that are understandable... But, it would be so cool to be able to customize errors much more in scalac...
Consider the following example that uses free monads:
Note how I can write the
update
function as part of the moduleRepository
by composing the other ADTs. I can do this asAccountRepo
is a free monad. Now let's have a look at thefreek
version ..The question is how do I define
update
here ? Note I am still within the moduleRepository
and have not yet defined my final co-product likePRG
orO
as in the examples offreek
. With freek, we deal with pure ADTs within the module which is not a monad - hence I need to wait till the final composition of the DSL (which will involve other modules as well) to create such functions.Is there any way in
freek
to have free monads like the above in the individual modules and then use theonionT
trick just for the monad transformation (using hk co-products) and interpreter composition ?