Why is defining an instance of Choice failing with unknown value?

83 Views Asked by At

UPDATE: I'm inlining the code here instead.

I'm trying to define an instance of Data.Profunctor.Choice where right is defined by calling left, but for some reason the compiler complains that left is unknown.

newtype StateTrans s i o = ST (Tuple s i → Tuple s o)

instance stFunctor ∷ Functor (StateTrans s a) where
  map f (ST st) = ST $ second f <<< st

instance stProfunctor ∷ Profunctor (StateTrans s) where
  dimap f g (ST st) = ST $ second g <<< st <<< second f

instance stChoice ∷ Choice (StateTrans s) where
  left (ST f) = ST lf
    where
    lf (Tuple s (Left a)) = let (Tuple s' b) = f (Tuple s a)
                            in Tuple s' (Left b)
    lf (Tuple s (Right c)) = Tuple s (Right c)
  -- Fails to compile with: Unknown value left
  right f = arr mirror <<< left f <<< arr mirror
    where
    mirror Left x = Right x
    mirror Right x = Left x

Probably a silly mistake but I've been looking at my code for so long, I can't figure out what's wrong.

(Of minor importance and unrelated: in the Right case for left, I have to unwrap and repackage the value so that it type-aligns. Adding a type ascription fails to compile too.)

Strangely enough, I have no problem doing the same for Strong, see:

instance stStrong ∷ Strong (StateTrans s) where
  first (ST f) = ST ff
    where
    ff (Tuple s (Tuple a c)) = let (Tuple s' b) = f $ Tuple s a
                               in Tuple s' (Tuple b c)
  second f = arr swap <<< first f <<< arr swap
2

There are 2 best solutions below

0
On BEST ANSWER

I'm not 100% sure as the pasted snippet doesn't include imports, but I suspect you have an import for Data.Profunctor.Choice (class Choice) rather than Data.Profunctor.Choice (class Choice, left, right).

Importing a class does not import its members implicitly, even though it is possible to define them in an instance without doing so.

3
On

First, to use arr you need to declare the Category instance for StateTrans s:

instance stSemigroupoid :: Semigroupoid (StateTrans s) where
  compose (ST f1) (ST f2) = ST $ f1 <<< f2

instance stCategory :: Category (StateTrans s) where
  id = ST $ \x -> x

For the second step, I needed to add more type annotations (not sure why they're necessary, but this way the build succeeded):

choiceLeft :: forall input output a s. (StateTrans s) input output -> (StateTrans s) (Either input a) (Either output a)
choiceLeft (ST f) = ST lf
  where
    lf (Tuple s (Left a))  = let (Tuple s' b) = f (Tuple s a)
                             in Tuple s' (Left b)
    lf (Tuple s (Right c)) = Tuple s (Right c)

choiceRight :: forall input output t s. (StateTrans s) input output -> (StateTrans s) (Either t input) (Either t output)
choiceRight f = amirror <<< choiceLeft f <<< amirror
  where
    mirror :: forall a b. Either a b -> Either b a
    mirror (Left x)  = Right x
    mirror (Right x) = Left x
    amirror :: forall a b. StateTrans s (Either b a) (Either a b)
    amirror = arr mirror

instance stChoice ∷ Choice (StateTrans s) where
  left  = choiceLeft
  right = choiceRight

Note: used PureScript version 0.11.7 and purescript-profunctor version 3.2.0.