Pregunta

Promoted datatypes have a fixed number of types that are a members of the promoted data kind . In this closed world would it make sense to support calling a function on a typeclass without a dictionary explicitly in scope?

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}

data DataType = Constructor

data DataTypeProxy (e :: DataType) = DataTypeProxy

class Class (e :: DataType) where
  classFunction :: DataTypeProxy e -> IO ()

-- this is the only instance that can be created
instance Class 'Constructor where
  classFunction _ = return ()

-- adding the Class constraint fixes the build break
-- disp :: Class e => DataTypeProxy e -> IO ()
disp :: DataTypeProxy e -> IO ()
disp = classFunction

main :: IO ()
main = disp (DataTypeProxy :: DataTypeProxy 'Constructor)

This contrived example doesn't work in GHC head. It's not at all surprising but it seems that the DataKind extension might make this possible.

test.hs:18:8:
    No instance for (Class e) arising from a use of `classFunction'
    Possible fix:
      add (Class e) to the context of
        the type signature for disp :: DataTypeProxy e -> IO ()
    In the expression: classFunction
    In an equation for `disp': disp = classFunction
¿Fue útil?

Solución

No. Doing allowing that would mean that phantom data types would need to be "tagged" with extra type information at runtime, and would produce ambiguity.

data DataType = Constructor | SomethingElse

data DataTypeProxy (e :: DataType) = DataTypeProxy 
...
instance Class 'SomethingElse where
   classFunction _ = putStrLn "hello world"

instance Class 'Constructor where
   classFunction _ = return ()

disp :: DataTypeProxy e -> IO ()
disp = classFunction

main = disp DataTypeProxy

what should this program do? Should it not compile? If not, then by adding a constructor to a type, we took a program that you would like to compile, and produced one that does not. If it should not compile then it has two valid behaviors.

main = disp (DataTypeProxy :: DataTypeProxy 'Constructor)

has only one possible interpretation...but it requires you to dispatch on phantom type. That is,

main = disp (DataTypeProxy :: DataTypeProxy 'SomethingElse)

is an identical program at the term level, but has different behavior. That breaks basically all the nice properties like parametrically. Typeclass based dispatching is a solution to this, since what instances in scope effect program behavior in a predictable (And semantically specified) way.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top