Created
May 22, 2017 22:32
-
-
Save boj/fedd3e9cea6f2fac43eb36806d0c680b to your computer and use it in GitHub Desktop.
Data.Bifunctor example of Either A B to Either A C
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
-- Given some function with an return type Either A B | |
buildTemplate :: Text -> IO (Either TemplateError [TmplExpr]) | |
-- Another function with the return type Either A C uses the prior function, | |
-- but due to the fact that the type signature varies, one cannot rely on | |
-- monadic chaining to work correctly for the failure case. | |
loadWeatherTemplates :: IO (Either TemplateError WeatherTemplates) | |
loadWeatherTemplates = | |
buildTemplate enterTemplate >>= \et -> | |
buildTemplate inputTemplate >>= \it -> | |
buildTemplate menuTemplate >>= \mt -> | |
buildTemplate weatherTemplate >>= \wt -> | |
-- do stuff, but sadly it won't compile | |
-- expects Either A C but is actually Either A B | |
-- First attempt - nested case statements | |
loadWeatherTemplates :: IO (Either TemplateError WeatherTemplates) | |
loadWeatherTemplates = | |
buildTemplate enterTemplate >>= \et -> | |
case et of | |
Left e -> return . Left $ e | |
Just et' -> | |
buildTemplate inputTemplate >>= \it -> | |
case it of | |
Left e -> return . Left $ e | |
Just it' -> | |
buildTemplate menuTemplate >>= \mt -> | |
case mt of | |
Left e -> return . Left $ e | |
Just mt' -> | |
buildTemplate weatherTemplate >>= \wt -> | |
case wt of | |
Left e -> return . Left $ e | |
Just wt' -> | |
-- finally, work with our values | |
-- this is ridiculous, especially since we could be parsing 200+ templates | |
-- Data.Bifunctor to the rescue | |
-- https://hackage.haskell.org/package/bifunctors-3.0/docs/Data-Bifunctor.html | |
-- second allows us to map the B to a C | |
-- allowing us to compose our Either A B with Either A C | |
second :: Bifunctor p => (b -> c) -> p a b -> p a c | |
-- Execute each 'buildTemplate' action | |
-- then use 'second' to map the Bs to a C | |
-- and compose them together so that in the case of an Either failure it propogates the TemplateError back up | |
loadWeatherTemplates :: IO (Either TemplateError WeatherTemplates) | |
loadWeatherTemplates = do | |
et <- buildTemplate enterTemplate | |
it <- buildTemplate inputTemplate | |
mt <- buildTemplate menuTemplate | |
wt <- buildTemplate weatherTemplate | |
return $ second (\v -> mkWeatherTemplates { wtEnter = v }) et >>= \w0 -> | |
second (\v -> w0 { wtInput = v }) it >>= \w1 -> | |
second (\v -> w1 { wtMenu = v }) mt >>= \w2 -> | |
second (\v -> w2 { wtWeather = v }) wt | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment