Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Coercing types in Persistent

Tags:

haskell

yesod

This is a "hello world" attempt that's currently failing - I am simply trying to run a selectList query on a SqLite database with the following code:

Database.Persist.Sqlite> runSqlite "database.sqlite" $ selectList [] [LimitTo 10]

<interactive>:46:1:
    Couldn't match expected type ‘SqlBackend’
                with actual type ‘PersistEntityBackend val0’
    The type variable ‘val0’ is ambiguous
    In the first argument of ‘print’, namely ‘it’
    In a stmt of an interactive GHCi command: print it

This almost seems too simple to screw-up... where did I go wrong?

like image 451
Athan Clark Avatar asked Jan 10 '23 08:01

Athan Clark


1 Answers

As you probably already know, one of Haskell's strengths is strong typing. The persistent-sqlite package takes this to an extreme (which, in my opinion, is a good thing) by requiring that table entries have their own data type.

For instance, if you have a table which holds fruits that looks like this

_______________________
|Fruit ID | Fruit Name|
-----------------------
|   0     | "apple"   |
|   1     | "orange"  |
-----------------------

and you do a query against this table using persistent-sqlite, the results should be stored in a corresponding Fruit type

data Fruit = Fruit { fruitName::String }

Just creating said data type isn't enough, there is a bunch of boilerplate code to create the needed class instances to actually use this. Rather than create this all by hand, you can use the template Haskell magic in the persistent-template library to create all of this for you.

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
Fruit
    name String
    deriving Show
|]

Your example code was actually correct, but was missing all of this stuff. Furthermore, the compiler didn't even know what type to try to use, so, the error message you got contained the following sentence

The type variable ‘val0’ is ambiguous

which was basically the compilers way of saying, "I don't know what type to extract the sql entry to." You can specify with an explicit type

print (fruits :: [Entity Fruit])

Finally, unfortunately, this code uses a bunch of GHC extensions. Putting this together, here is a more complete working simplest example.

{-# LANGUAGE GADTs                      #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE QuasiQuotes                #-}
{-# LANGUAGE TemplateHaskell            #-}
{-# LANGUAGE TypeFamilies               #-}
import           Control.Monad.IO.Class  (liftIO)
import           Database.Persist.Sqlite
import           Database.Persist.TH

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
Fruit
    name String
    deriving Show
|]


main :: IO ()
main = runSqlite "fruitDb.sqlite" $ do
    fruits <- selectList [] [LimitTo 10]
    liftIO $ print (fruits :: [Entity Fruit])

and just to be complete, here is how to populate the sqlite db to test this.

> sqlite3 fruitDb.sqlite

sqlite> create table fruit (id, name);
sqlite> insert into fruit values (0, "apple");
sqlite> insert into fruit values (1, "orange");
like image 86
jamshidh Avatar answered Jan 17 '23 19:01

jamshidh