goldfirere / singletons

Fake dependent types in Haskell using singletons

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Problem generating `SDecide` instances for custom promoted types

cgohla opened this issue · comments

I followed the README to get around the Symbol/Text problem for some types in an application. Now I'm trying to generate SDecide instances for my custom promoted type, but it seems the instances are generated incorrectly, i.e., the name mapping for the promoted types is not applied correctly.

I have a minimal example to demonstrate the problem here:

{-# LANGUAGE DataKinds                #-}
{-# LANGUAGE FlexibleContexts         #-}
{-# LANGUAGE GADTs                    #-}
{-# LANGUAGE KindSignatures           #-}
{-# LANGUAGE OverloadedStrings        #-}
{-# LANGUAGE QuasiQuotes              #-}
{-# LANGUAGE ScopedTypeVariables      #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TemplateHaskell          #-}
{-# LANGUAGE TypeApplications         #-}
{-# LANGUAGE TypeFamilies             #-}
{-# LANGUAGE UndecidableInstances     #-}
module Main (main) where

import           Data.Singletons.TH         (genSingletons, singDecideInstances,
                                             singletons)
import           Data.Singletons.TH.Options (defaultOptions,
                                             defunctionalizedName,
                                             promotedDataTypeOrConName,
                                             withOptions)
import           Data.String                (fromString)
import           Data.String.Singletons     (FromString)
import           Data.Text                  (Text)
import           GHC.TypeLits.Singletons    (Symbol)
import           Language.Haskell.TH        (Name)

-- Term-level
newtype Message = MkMessage Text
-- Type-level
newtype PMessage = PMkMessage Symbol

$(let customPromote :: Name -> Name
      customPromote n
        | n == ''Message  = ''PMessage
        | n == 'MkMessage = 'PMkMessage
        | n == ''Text     = ''Symbol
        | otherwise       = promotedDataTypeOrConName defaultOptions n

      customDefun :: Name -> Int -> Name
      customDefun n sat = defunctionalizedName defaultOptions (customPromote n) sat in

  withOptions defaultOptions{ promotedDataTypeOrConName = customPromote
                            , defunctionalizedName      = customDefun
                            } $ do
    decs1 <- genSingletons [''Message]
    decs2 <- singletons [d|
               hello :: Message
               hello = MkMessage "hello"
               |]
    decs3 <- singDecideInstances [''Message]
    return $ decs1 ++ decs2 ++ decs3)

main :: IO ()
main = putStrLn "Hello, Haskell!"

This yields the following errors:

$ cabal build
Resolving dependencies...
Build profile: -w ghc-9.2.4 -O1
In order, the following will be built (use -v for more details):
 - singletons-decide-instances-0.1.0.0 (exe:singletons-decide-instances) (file app/Main.hs changed)
Preprocessing executable 'singletons-decide-instances' for singletons-decide-instances-0.1.0.0..
Building executable 'singletons-decide-instances' for singletons-decide-instances-0.1.0.0..
[1 of 1] Compiling Main             ( app/Main.hs, /home/bgohla/src/scratch/singletons-decide-instances/dist-newstyle/build/x86_64-openbsd/ghc-9.2.4/singletons-decide-instances-0.1.0.0/x/singletons-decide-instances/build/singletons-decide-instances/singletons-decide-instances-tmp/Main.o, /home/bgohla/src/scratch/singletons-decide-instances/dist-newstyle/build/x86_64-openbsd/ghc-9.2.4/singletons-decide-instances-0.1.0.0/x/singletons-decide-instances/build/singletons-decide-instances/singletons-decide-instances-tmp/Main.dyn_o )

app/Main.hs:32:2: error:
    • Couldn't match kind ‘PMessage’ with ‘Message’
      Expected kind ‘Message -> *’,
        but ‘SMessage’ has kind ‘PMessage -> *’
    • In the first argument of ‘Data.Type.Coercion.TestCoercion’, namely
        ‘(SMessage :: Message -> ghc-prim-0.8.0:GHC.Types.Type)’
      In the instance declaration for
        ‘Data.Type.Coercion.TestCoercion (SMessage :: Message
                                                      -> ghc-prim-0.8.0:GHC.Types.Type)’
   |
32 | $(let customPromote :: Name -> Name
   |  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...

app/Main.hs:32:2: error:
    • Couldn't match kind ‘PMessage’ with ‘Message’
      Expected kind ‘Message -> *’,
        but ‘SMessage’ has kind ‘PMessage -> *’
    • In the first argument of ‘Data.Type.Equality.TestEquality’, namely
        ‘(SMessage :: Message -> ghc-prim-0.8.0:GHC.Types.Type)’
      In the instance declaration for
        ‘Data.Type.Equality.TestEquality (SMessage :: Message
                                                      -> ghc-prim-0.8.0:GHC.Types.Type)’
   |
32 | $(let customPromote :: Name -> Name
   |  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...

So it seems singDecideInstances disregards the custom name mapping we defined. I have not dug into the implementation to understand what goes wrong.

A Cabal project with a freeze file is here https://github.com/cgohla/singletons-decide-instances.

I am able to reproduce this. Curiously, this does not happen if you generate SDecide instances by way of deriving Eq. That is, the following does work:

    decs2 <- singletons [d|
               hello :: Message
               hello = MkMessage "hello"

               deriving instance Eq Message
               |]

I'll need to take a closer look to see why singDecideInstances isn't behaving identically.

I am able to reproduce this. Curiously, this does not happen if you generate SDecide instances by way of deriving Eq. That is, the following does work:

    decs2 <- singletons [d|
               hello :: Message
               hello = MkMessage "hello"

               deriving instance Eq Message
               |]

That would be a neat workaround, but doesn't work for me. I get this error instead:

Build profile: -w ghc-9.2.4 -O1
In order, the following will be built (use -v for more details):
 - singletons-decide-instances-0.1.0.0 (exe:singletons-decide-instances) (file app/Main.hs changed)
Preprocessing executable 'singletons-decide-instances' for singletons-decide-instances-0.1.0.0..
Building executable 'singletons-decide-instances' for singletons-decide-instances-0.1.0.0..
[1 of 1] Compiling Main             ( app/Main.hs, /home/bgohla/src/scratch/singletons-decide-instances/dist-newstyle/build/x86_64-openbsd/ghc-9.2.4/singletons-decide-instances-0.1.0.0/x/singletons-decide-instances/build/singletons-decide-instances/singletons-decide-instances-tmp/Main.o, /home/bgohla/src/scratch/singletons-decide-instances/dist-newstyle/build/x86_64-openbsd/ghc-9.2.4/singletons-decide-instances-0.1.0.0/x/singletons-decide-instances/build/singletons-decide-instances/singletons-decide-instances-tmp/Main.dyn_o )

app/Main.hs:49:2: error: Q monad failure
   |
49 | $(let customPromote :: Name -> Name
   |  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...

app/Main.hs:49:2: error: Cannot find type annotation for ==
   |
49 | $(let customPromote :: Name -> Name
   |  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...

NB I'm stuck on GHC 9.2.4 for now. Is there any way I can use your fix @RyanGlScott (without forking and patching the library myself?)

I'll need to take a closer look to see why singDecideInstances isn't behaving identically.

Thanks for the quick response anyway 👍 .

I am able to reproduce this. Curiously, this does not happen if you generate SDecide instances by way of deriving Eq. That is, the following does work:

    decs2 <- singletons [d|
               hello :: Message
               hello = MkMessage "hello"

               deriving instance Eq Message
               |]

That would be a neat workaround, but doesn't work for me. I get this error instead:

Build profile: -w ghc-9.2.4 -O1
In order, the following will be built (use -v for more details):
 - singletons-decide-instances-0.1.0.0 (exe:singletons-decide-instances) (file app/Main.hs changed)
Preprocessing executable 'singletons-decide-instances' for singletons-decide-instances-0.1.0.0..
Building executable 'singletons-decide-instances' for singletons-decide-instances-0.1.0.0..
[1 of 1] Compiling Main             ( app/Main.hs, /home/bgohla/src/scratch/singletons-decide-instances/dist-newstyle/build/x86_64-openbsd/ghc-9.2.4/singletons-decide-instances-0.1.0.0/x/singletons-decide-instances/build/singletons-decide-instances/singletons-decide-instances-tmp/Main.o, /home/bgohla/src/scratch/singletons-decide-instances/dist-newstyle/build/x86_64-openbsd/ghc-9.2.4/singletons-decide-instances-0.1.0.0/x/singletons-decide-instances/build/singletons-decide-instances/singletons-decide-instances-tmp/Main.dyn_o )

app/Main.hs:49:2: error: Q monad failure
   |
49 | $(let customPromote :: Name -> Name
   |  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...

app/Main.hs:49:2: error: Cannot find type annotation for ==
   |
49 | $(let customPromote :: Name -> Name
   |  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...

[...]

Nevermind. Adding Data.Eq.Singletons fixed it 🤦