I have a an aggregate type named Step
and from it, some concrete types are created:
class (Eq q, User a, User b, User c) => (Step q a b c) where
{-# MINIMAL performerA, performerB, performerC, completionDate #-}
performerA :: Lens' q a
performerB :: Lens' q b
performerC :: Lens' q (Maybe c)
data Step1
makeLenses ''Step1
instance Step Step1 UserType1 UserType2 UserType3 where
...
data Step2
makeLenses ''Step2
instance Step Step2 UserType1 UserType2 UserType4 where
...
My factory function takes some arguments and returns a step corresponding to some criteria expressed using case
:
mkStep :: (User a, User b, User c, Step q b a c)
=> Package a b
-> b
-> Maybe c
-> Either ValidationError q
mkStep package performer' nextPerformer' = do
return step
where
step = case (package ^. currentPerformer . memberType, performer' ^. memberType, nextPerformer' ^. memberType) of
(MemberType3, MemberType2, MemberType4)
-> Step1 performer' (package ^. currentPerformer) nextPerformer'
(MemberType3, MemberType2, MemberType1)
-> Step2 performer' (package ^. currentPerformer) nextPerformer'
Some extra info on other types involved in constructing a Step
:
data MemberType = MemberType1
| MemberType2
| MemberType3
| MemberType4
deriving (Show, Eq)
class (Eq a, Show a) => User a where
{-# MINIMAL memberType, userId #-}
memberType :: Lens' a MemberType
userId :: Lens' a MemberId
data UserType1
makeLenses ''UserType1
instance User UserType1 where
...
data UserType2
makeLenses ''UserType2
instance User UserType2 where
...
data UserType3
makeLenses ''UserType3
instance User UserType3 where
...
data UserType4
makeLenses ''UserType4
instance User UserType4 where
...
data Package a b = Package
{ _currentPerformer :: a
, _nextPerformer :: Maybe b
} deriving (Show, Eq)
makeLenses ''Package
However, when I attempt building the code, I get the following:
> stack build
Building all executables for 'ss-model-ddd' once. After a successful build of all of them, only specified executables will be rebuilt.
ss-model-ddd> build (lib exe)
Preprocessing library for ss-model-ddd-0.1.0.0..
Building library for ss-model-ddd-0.1.0.0..
[7 of 7] Compiling Steps
/home/aoaddeola/ss-model-ddd/src/Steps.hs:103:10: error:
• Couldn't match expected type ‘q’
with actual type ‘Step2’
‘q’ is a rigid type variable bound by
the type signature for:
mkStep :: forall a b c q.
(User a, User b, User c, Step q b a c) =>
Package a b -> b -> Maybe c -> Either ValidationError q
at src/Steps.hs:(96,1)-(100,34)
• In the first argument of ‘return’, namely ‘step’
In a stmt of a 'do' block: return step
In the expression: do return step
• Relevant bindings include
mkStep :: Package a b
-> b -> Maybe c -> Either ValidationError q
(bound at src/Steps.hs:101:1)
|
103 | return step
| ^^^^
/home/aoaddeola/ss-model-ddd/src/Steps.hs:109:18: error:
• Couldn't match type ‘Step1’
with ‘Step2’
Expected: Step2
Actual: Step1
• In the expression:
Step1
performer' (package ^. currentPerformer) nextPerformer'
In a case alternative:
(MemberType3, MemberType2, MemberType4)
-> Step1
performer' (package ^. currentPerformer) nextPerformer'
In the expression:
case
(package ^. currentPerformer . memberType, performer' ^. memberType,
nextPerformer' ^. memberType)
of
(MemberType3, MemberType2, MemberType1)
-> Step2
performer' (package ^. currentPerformer) nextPerformer'
(MemberType3, MemberType2, MemberType4)
-> Step1
performer' (package ^. currentPerformer) nextPerformer'
|
109 | -> Step1 performer' (package ^. currentPerformer) nextPerformer'
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Finding it difficult to figure out what the problem could be. My first culprit is the fact that I'm using Lenses, which are only available at runtime (I guess).
Any solution to the issue will be appreciated.
CodePudding user response:
This doesn't really have anything to do with lenses. You've written a mkStep
function that's a lot like:
mkFoo :: (Show q) => Bool -> q
mkFoo b = case b of True -> 'c'
False -> ()
In other words, you've claimed mkStep
will provide whatever (rigid AKA user-specified) type Show q => q
the caller requests, when in fact you're going to provide a q
determined by your function, not the caller, with a type that's based on a runtime input at that.
This is broken in two ways. You can't claim to provide any q
the caller wants and then provide only a specific q
(e.g., Step2
), and you can't write a case statement that evaluates to expressions of two different types (Step1
versus Step2
) depending on a scrutinee of a single type (MemberType, MemberType, Membertype)
.