Open the door with types, part 4
To avoid passing SingState explicitly, you can define a type class whose method returns SingState from State.
class SingStateI (state :: State) where
singState :: SingState state
instance SingStateI 'Opened where
singState = SOpened
instance SingStateI 'Closed where
singState = SClosed
instance SingStateI 'Locked where
singState = SLocked
You no longer need to pass SingState explicity to SomeDoor.
data SomeDoor = forall state. SingStateI state => SomeDoor (Door state)
forceOpen will have SingStateI as its context instead of taking SingState explicity.
forceOpen :: forall state. SingStateI state => Text -> Door state -> Maybe (Door 'Opened)
forceOpen key door =
case singState @state of
SOpened -> Just door
SClosed -> Just $ open $ knock door
SLocked -> case unlock key $ knock door of
Right closedDoor -> Just $ open closedDoor
Left _ -> Nothing
forceOpenSomeDoor :: Text -> SomeDoor -> Maybe (Door 'Opened)
forceOpenSomeDoor key (SomeDoor door) = forceOpen key door
Now we can write a type-safe door, but writing State, SingState and SingStateI manually every time is troublesome. I’ll use singletons to define them automatically in the next post.