-
-
Save yawnt/63bcffaa7683d2ac3d48b8f6e90c3337 to your computer and use it in GitHub Desktop.
Why is MonadError helpful?
This file contains 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
/** | |
* Let's say I can read things from a "Store" | |
*/ | |
trait Store[F] { | |
def read(path: Path): F[String] | |
} | |
/** | |
* Now I want to read more specific things, like a Configuration | |
* | |
* If I can constrain the "F" in my store to be a Monad (a Functor would be enough) | |
* Then I can parse the configuration and return a Configuration object | |
* | |
* However: | |
* | |
* - I have no ways to return parsing errors | |
* - If I have a Store that already provides a notion of Error | |
* things might be a bit clumsy see == 1 == | |
*/ | |
object ConfigurationStorage { | |
val path: Path = Path("configuration") | |
def fromStore[F[_] : Monad](store: Store[F]): F[Option[Configuration]] = | |
store.read(path).map(parseConfiguration) | |
def parseConfiguration(conf: String): Option[Configuration] = ??? | |
} | |
/** | |
* == 1 == | |
* | |
* A Store with a Monad knowing about errors | |
* Using this store with the ConfigurationStorage would return | |
* | |
* val store: IOErrorStore = ??? | |
* // too bad we can't use "Throwable" to indicate that the reading operation failed! | |
* val configuration: IO[Throwable \/ Option[Configuration]] = | |
* ConfigurationStorage.fromStore(store) | |
*/ | |
trait IOErrorStore { | |
/** | |
* this type can do IO and store errors if | |
* a path can't be read. | |
* It is possible to implement the MonadError interface | |
* for this type | |
*/ | |
type IOError[T] = IO[Throwable \/ T] | |
def read(path: Path): IOError[String] | |
} | |
/** | |
* Now, if I have a MonadError type I can do the following | |
* and register errors for any Monad that is also a MonadError | |
* (don't take this literally, a bit more is necessary to have all the types align correctly) | |
*/ | |
object ConfigurationStorage { | |
val path: Path = Path("configuration") | |
def fromStore[F[_,_](store: Store[F])(implicit monad: MonadError[F, Throwable]): F[Configuration] = | |
store.read(path).flatMap { string => parseConfiguration(string) match { | |
case -\/(t) => monad.raiseError(t) | |
case \/-(configuration) => monad.point(configuration) | |
}} | |
def parseConfiguration(conf: String): Throwable \/ Configuration = ??? | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment