Using Generic Deriving with a Record Haskell
Asked Answered
C

1

8

I am basically attempting to see if I can emulate an ORM framework within Haskell, so that if a user wants to make a database model, they would do something like this

data Car = Car {
        company :: String, 
        model :: String, 
        year :: Int
        } deriving (Model)

Where the table would be "Car", and the columns would be the company,model,year

To do this within Haskell, you have to use a combination of classes and generics, and this is where I am getting stuck. Using this tutorial (http://www.haskell.org/ghc/docs/7.4.1/html/users_guide/generic-programming.html), I came up with this (which was just basically copying and renaming so I can get the code working)

{-# LANGUAGE DeriveGeneric, TypeOperators, TypeSynonymInstances, FlexibleInstances #-}

module Main where
import GHC.Generics

class SModel b where
        s_new :: b -> IO()

instance SModel Int where
        s_new s = putStrLn s++":Int"

instance SModel Integer where
        s_new s = putStrLn s++":Integer"

instance SModel String where
        s_new s = putStrLn s++":String"    

class Model m where
        new :: m a -> IO()

instance Model U1 where
        new U1 = putStrLn "unit"

instance (Model a, Model b) => Model (a :*: b) where
        new (a :*: b) = do
                new a
                new b

instance (Model a, Model b) => Model (a :+: b) where
        new (L1 x) = new x
        new (R1 x) = new x

instance (Model a) => Model (M1 i c a) where
        new (M1 x) = new x

instance (SModel a) => Model (K1 i a) where
        new (K1 x) = s_new x

data Car = Car {
        company :: String, 
        model :: String, 
        year :: Int
        } deriving (Model)

The above code will produces the error

Cannot derive well-kinded instance of form `Model (Car ...)'
      Class `Model' expects an argument of kind `* -> *'
    In the data declaration for `Car'

And I am kind of stuck at this point, I believe I have already instantiated all of the required Generic types to cover a record

Collaborationist answered 2/7, 2013 at 2:38 Comment(2)
You cannot use Model in a deriving clause. Instead, you should derive Generic and then provide a default instance for SModel. See the GHC Users Guide on "Generic defaults". There's also some preliminary improved documentation at github.com/kosmikus/generic-deriving/blob/master/src/Generics/…Carburet
So Haskell has no official support for using deriving for anything apart from show, eq (and some of the other builtin derivables)Collaborationist
P
7

As kosmikus said in his comment, you cannot derive Model directly. First you need a 'front-end' class for Model providing a generic default, which could look like this:

class FModel a where
    fnew :: a -> IO()

    default new :: (Generic a, Model (Rep a)) => a -> IO()
    fnew = new . from

Then you can just do:

Car = ... deriving Generic
instance FModel Car

And you have the desired instance.

Polis answered 2/7, 2013 at 12:42 Comment(1)
If even that's too much boilerplate (it gets more if you have more classes you want to "derive"), you can use TemplateHaskell to generate all the empty instances, although I wouldn't do it if you only have one class.Indoaryan

© 2022 - 2024 — McMap. All rights reserved.