-- | Partial functions that make common types in Lincoln. Some data -- types in Lincoln are deeply nested, with TextNonEmpty nested inside -- of a newtype, nested inside of a NonEmptyList, nested inside -- of... :) All the nesting ensures to the maximum extent possible -- that the type system reflects the restrictions that exist on -- Penny's data. For example, it would make no sense to have an empty -- account (that is, an account with no sub-accounts) or a sub-account -- whose name is an empty Text. -- -- The disadvantage of the nesting is that building these data types -- can be tedious if, for example, you want to build some data within -- a short custom Haskell program. Thus, this module. module Penny.Lincoln.Builders ( crashy , account , commodity ) where import Control.Monad.Exception.Synchronous as Ex import qualified Data.List.Split as S import Data.List.NonEmpty (NonEmpty((:|))) import qualified Penny.Lincoln.Bits as B import Penny.Lincoln.TextNonEmpty (TextNonEmpty(TextNonEmpty)) import Data.Text (pack) import qualified Data.Traversable as T -- | Makes a function partial. Use if you don't want to bother dealing -- with the Exceptional type. crashy :: Show e => Ex.Exceptional e a -> a crashy = Ex.resolve (error . show) -- | Create an Account. You supply a single String, with colons to -- separate the different sub-accounts. account :: String -> Ex.Exceptional String B.Account account input = do subStrs <- case S.splitOn ":" input of [] -> error "splitOn returned an empty list; should never happen" []:[] -> Ex.throw "account name is null" (s:ss) -> return $ s :| ss let makeSub s = case s of [] -> Ex.throw $ "sub account name is null from account: " ++ input (c:cs) -> return $ B.SubAccountName (TextNonEmpty c (pack cs)) subs <- T.traverse makeSub subStrs return $ B.Account subs -- | Create a Commodity. You supply a single String, with colons to -- separate the different sub-commodities. commodity :: String -> Ex.Exceptional String B.Commodity commodity input = do subStrs <- case S.splitOn ":" input of [] -> error "splitOn returned an empty list; should never happen" []:[] -> Ex.throw "account name is null" (s:ss) -> return $ s :| ss let makeSub s = case s of [] -> Ex.throw $ "sub account name is null from account: " ++ input (c:cs) -> return $ B.SubCommodity (TextNonEmpty c (pack cs)) subs <- T.traverse makeSub subStrs return $ B.Commodity subs