Dhall-haskell: Read Dhall into higher-kinded datatype with Generic

Created on 23 Jul 2019  路  3Comments  路  Source: dhall-lang/dhall-haskell

Higher kinded datatypes allow for much easier processing of similar types, as shown here:
https://chrispenner.ca/posts/hkd-options

It would be nice to be able to have Generic instance of Interpret that reads any higher kinded type.
The difference seems simple on the surface. Instead of:

data MyConfig = MyConfig {
    port :: Int
  , host :: String
  }

We use:

data MyConfig f = MyConfig {
    port  :: f Int
  , host :: f String
  }

Reading a plain data structure instead of this type:

interpret :: Type MyConfig -> Text -> IO MyConfig

gets this type:

interpret :: Type (MyConfig Identity) -> Text -> IO (MyConfig Identity)

If we want to use interpret to read Dhall with arbitrary annotation attached to datatype (#1148), that gets tugged-along, then we get:

data Annotated a = Annotated { ann:: Annotation, content :: a }
interpret :: Type (MyConfig Annotated) -> Text -> IO (MyConfig Annotated)

Most helpful comment

This is possible without any changes to the Haskell API if you use GHC version 8.6 or later (which provides the QuantifiedConstraints extension).

Here is a slightly modified version of the example you gave:

{-# LANGUAGE DeriveAnyClass        #-}
{-# LANGUAGE DeriveGeneric         #-}
{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE TypeApplications      #-}
{-# LANGUAGE UndecidableInstances  #-}

import Dhall (Generic, Interpret(..), Natural)
import Data.Functor.Identity (Identity(..))

import qualified Data.Coerce
import qualified Dhall

data MyConfig f = MyConfig
    { port :: f Natural
    , host :: f String
    } deriving (Generic)

instance Interpret a => Interpret (Identity a) where
    autoWith = Data.Coerce.coerce (autoWith @a)

instance (forall a . Interpret a => Interpret (f a)) => Interpret (MyConfig f)

data Annotated a = Annotated { ann :: String, content :: a }
    deriving (Generic, Interpret)

main :: IO ()
main = do
    x <- Dhall.input Dhall.auto "{ port = 80, host = \"example.com\" }"

    print (runIdentity (port x))

    y <- Dhall.input Dhall.auto "{ port = { ann = \"Hi!\", content = 80 }, host = { ann = \"Bye!\", content = \"example.com\" } }"

    print (ann (host y))

... which produces this output:

"Bye!"

All 3 comments

This is possible without any changes to the Haskell API if you use GHC version 8.6 or later (which provides the QuantifiedConstraints extension).

Here is a slightly modified version of the example you gave:

{-# LANGUAGE DeriveAnyClass        #-}
{-# LANGUAGE DeriveGeneric         #-}
{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE TypeApplications      #-}
{-# LANGUAGE UndecidableInstances  #-}

import Dhall (Generic, Interpret(..), Natural)
import Data.Functor.Identity (Identity(..))

import qualified Data.Coerce
import qualified Dhall

data MyConfig f = MyConfig
    { port :: f Natural
    , host :: f String
    } deriving (Generic)

instance Interpret a => Interpret (Identity a) where
    autoWith = Data.Coerce.coerce (autoWith @a)

instance (forall a . Interpret a => Interpret (f a)) => Interpret (MyConfig f)

data Annotated a = Annotated { ann :: String, content :: a }
    deriving (Generic, Interpret)

main :: IO ()
main = do
    x <- Dhall.input Dhall.auto "{ port = 80, host = \"example.com\" }"

    print (runIdentity (port x))

    y <- Dhall.input Dhall.auto "{ port = { ann = \"Hi!\", content = 80 }, host = { ann = \"Bye!\", content = \"example.com\" } }"

    print (ann (host y))

... which produces this output:

"Bye!"

Thanks @Gabriel439. Will be happy to use it!

@mgajda: You're welcome! 馃檪

Was this page helpful?
0 / 5 - 0 ratings