Skip to content

Instantly share code, notes, and snippets.

@fanf
Created August 14, 2016 23:40
Show Gist options
  • Save fanf/845273a15377347a04375d51a0139b78 to your computer and use it in GitHub Desktop.
Save fanf/845273a15377347a04375d51a0139b78 to your computer and use it in GitHub Desktop.
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
}
}
}
@fanf
Copy link
Author

fanf commented Aug 17, 2016

(ok, my interpretation of the thread ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment