Reputation: 1155
Is there a way to define bind that only affects a part of a data structure?
For example, say we have a mock Computation data type that has a Name and a Result.
type Name = String
data Error = TimeOut | NotEnoughMemory | DivByZero
type Result a = Either Error a
data Computation a = Computation Name (Result a)
Since the computation could fail but we might still be interested in its name, it can't be part of Result and must be part of Computation. On the other hand it would be also convenient to bind and validate results of Computations directly like you would do with a regular Either type. Is there a way to do that?
Upvotes: 2
Views: 81
Reputation: 60463
I think you may want
type Computation = ExceptT Error (Writer Name)
-- in familiar types:
-- Computation a ⋍ (Either Error a, Name)
Notice that Name
has to be a Monoid
now. That makes sense, since
liftA2 (+) comp1 comp2
, which adds the results of two sub-computations, needs to also have a name. What the Writer
component does is concatenates (with the monoid's <>
-- so not necessarily just string concatenation) the names of the two sub-computations. Perhaps you would use, for example:
newtype Name = Name [String]
deriving (Semigroup, Monoid)
You would probably also want a way to define the name of some computation:
name :: String -> Computation a -> Computation a
name n = mapExceptT (censor (const (Name [n])))
For example:
two :: Computation Int
two = name "two" $ pure 2
someComp :: Computation Int
someComp = name "two plus two" $ liftA2 (+) two two
Upvotes: 2
Reputation: 16645
No you probably can't define any useful instance Monad Computation
, since you cannot define pure :: a -> Computation a
from the Applicative
class (which is a superclass of Monad
); how would you instantiate the Name
given just a value of type a
?
It is a Functor
though (and you can derive it automatically). With more context there might be a different abstraction that would be more useful.
Upvotes: 2