Created
August 14, 2016 23:40
-
-
Save fanf/845273a15377347a04375d51a0139b78 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object Test { | |
/* | |
* This is a clearer version of https://gist.github.com/fanf/f26eee67ae33bf54e363e4e5dce01388 | |
*/ | |
type Maybe[A] = Xor[String, A] | |
final case class Query(value: String) | |
final case class Result(value: String) | |
final case class Miss(value: String) | |
sealed trait MayFail[A] | |
final object MayFail { | |
final case class Parse(query: String) extends MayFail[Maybe[Query]] | |
final case class Get(query: Query) extends MayFail[Maybe[List[Either[Miss, Result]]]] | |
} | |
import cats.std.all._ | |
import cats.free.Free | |
import freek._ | |
type P = MayFail :|: FXNil //yeah, talk me again about free monads for such domain... :) | |
val P = Program[P] | |
/* | |
* Now, I want a method "search" that returns a Maybe[List[Either[Miss,Result]]] | |
* | |
* In a "standard" scala program, it would be: | |
* for { | |
* query <- MyParser.parse(input) // Maybe[Query] | |
* results <- MyBackend.get(query) // Maybe[List[Either[Miss,Result]]] | |
* } yield { | |
* results | |
* } | |
* | |
* But there is no good way in Freek to differentiate between | |
* data types that should be in the stack and data types that | |
* just happen to be sufficiently traversable. | |
* | |
* So, the general problem is to be able to tell Freek in some | |
* way "ignore the N last traversable types, they are actual | |
* opaque containers for that line. | |
* | |
* Typically, in a return type: Xor[String, Option[List[Xor[BusinessTypeA, BusinessTypeB]]]] | |
* I want to be able to tell: Freek, the stack is just about dealing with | |
* Xor[String, Option], the List[Xor] is opaque for you and should be | |
* manipulated like an Int would be. | |
* | |
*/ | |
/* | |
* Working solution: this is the stack I must declare, and only | |
* to be able to say that I don't want to consider List at all. | |
* It works, but it is really too convoluted and boilerplaty | |
* to be a general solution. In a program with 3x that number of | |
* lines and more complex types/data, it is adding a big price. | |
* | |
* That said, I'm not even sure to understand why I don't have to add | |
* "Either" in the stack to remove it as it is done for List. | |
*/ | |
type WORKS = Maybe :&: List :&: Bulb | |
def search1(input: String) = { | |
for { | |
lquery <- MayFail.Parse(input).freek[P].onionT[WORKS].peelRight | |
query = lquery.head // my linter will go mad here | |
results <- MayFail.Get(query).freek[P].onionT[WORKS].peelRight | |
} yield { | |
results | |
} | |
} | |
// this is the actual stack I would like to have: | |
// "Maybe" is the only type to combine here | |
type WANTED = Maybe :&: Bulb | |
/* | |
* The first idea is to only take the first element of | |
* the stack, but here, result is of type: | |
* Maybe[List[Either]], where I would like it to be: | |
* List[Either]. | |
*/ | |
def search2(input: String) = { | |
for { | |
query <- MayFail.Parse(input).freek[P].onionT[WANTED] | |
result <- MayFail.Get(query).freek[P].onionP[WANTED] | |
} yield { | |
result | |
} | |
} | |
/* | |
* I would like something like belove, where | |
* freeko1 tells "look for one layer depth", | |
* freeko2 tells "look for two layers depth", | |
* etc. | |
* And freeko tell as today: go all layers. | |
*/ | |
def search3(input: String) = { | |
for { | |
query <- MayFail.Parse(input).freeko[P,WANTED] | |
/* | |
* Here, I really wanted to put "freeko1" given | |
* the above logic, but it seems to be the same as | |
* onionP and we saw that opionP keep a layer of | |
* Maybe that should be removed. | |
* | |
* So clearly I'm mixing up the "return" type | |
* of Get and the stack of transformer. Because | |
* there is a Maybe[Maybe[...]] that must be | |
* transformed. | |
* | |
* So, it points that whatever the solution, user will | |
* likelly spend some time trying freeko"N +/- 2", with | |
* N "a good feeling about how layered is my onion". | |
*/ | |
result <- MayFail.Get(query).freeko2[P,WANTED] //of course does not compile | |
} yield { | |
result | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
(ok, my interpretation of the thread ;)