pa-ba / compdata

Haskell library implementing "Data Types a la Carte"
http://hackage.haskell.org/package/compdata
Other
89 stars 27 forks source link

makeShowHF doesn't work for lists #34

Open liarokapisv opened 3 years ago

liarokapisv commented 3 years ago

First of all thanks for the incredible library! I am using it on a compiler project and the Data.Comp.Multi modules have been incredibly useful for manipulting the AST.

One problem that I have encountered is that makeShowHF does not work for lists or maybe values. All other derivations work perfectly!

Here is a minimal example:

data Expr
data Stmt

data T r i where
    ExprInt :: Int -> T r Expr
    ExprAdd :: r Expr -> r Expr -> T r Expr
    ProcCall :: String -> [r Expr] -> T r Stmt

This fails with:

    • Couldn't match expected type ‘Data.Comp.Multi.HFunctor.K
                                      String i0’
                  with actual type ‘[Data.Comp.Multi.HFunctor.K String Expr]’
    • In the first argument of ‘Data.Comp.Multi.HFunctor.unK’, namely
        ‘x_a4jN’
      In the expression: Data.Comp.Multi.HFunctor.unK x_a4jN
      In the second argument of ‘compdata-0.12.1:Data.Comp.Multi.Derive.Show.showConstr’, namely
        ‘[show x_a4jM, Data.Comp.Multi.HFunctor.unK x_a4jN]’

I can get around this via hacks but it's not really ergonomic:

data Expr
data Stmt

data T r i where
    (:-:) :: r a -> r [a] -> T r [a]
    Nil :: T r [a]
    ExprInt :: Int -> T r Expr
    ExprAdd :: r Expr -> r Expr -> T r Expr
    --not really equivalent!
    ProcCall :: String -> r [Expr] -> T r Stmt

The equivalent seems to work for makeShowF so I thought maybe this was worth reporting.

liarokapisv commented 3 years ago

After digging through the code it seems the following lines in the makeShowHF function create the problem:

mkShow (isFArg, var)
                | isFArg = [|unK $var|]
                | otherwise = [| show $var |]

In our case [r Expr] does indeed contain r so the isFArg is chosen. I can fix my problem by modifying the above like so:

mkShow (ty, isFArg, var)
                | isFArg = case ty of
                                AppT ListT _ -> [|show (fmap (NoQuotes . unK) $var) |]
                                _  -> [|unK $var|]
                | otherwise = [| show $var |]

NoQuotes is a simple wrapper over string that does not print quotes. This has the advantage that the ListT implementation holds for any Showable functor and not just lists. I think this would be a great enhancement to the library. Off the top of my mind one should be able to distinguise the r _ and t (r _) cases in the mkShow sub-function and be able to query if t is a Functor. It should be a minimal change overall. I can probably do it myself if you agree with this and are willing to accept a PR.

liarokapisv commented 3 years ago

Another option is something like this:

newtype NoQuotes = NoQuotes String
instance Show NoQuotes where show (NoQuotes str) = str

newtype W f = W { unW :: f }

instance Show (W (K String i)) where
    show = unK . unW

instance (Functor f, Show (f NoQuotes)) => Show (W (f (K String i))) where
    show = show . fmap (NoQuotes . unK) . unW
mkShow (isFArg, var)
        | isFArg = [| show (W $var) |]
        | otherwise = [| show $var |]