Composite Primary Key in Yesod - haskell

I am fairly new to Haskell and have been experimenting with yesod for about a week now. I have been trying to connect to an existing database that has a composite primary key in sqlite. I managed to get the code to work with Database.Persist.Sqlite as a standalone application.
Here is the code that works as a standalone application using persistent-sqlite.
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DeriveGeneric #-}
import Control.Monad.IO.Class (liftIO)
import Database.Persist
import Database.Persist.Sqlite
import Database.Persist.TH
import System.Environment
import Data.Text (Text,pack)
import Data.Time (UTCTime)
share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
Movie
title Text maxlen=20
year Int maxlen=11
genre Text Maybe maxlen=128
mpaa Text Maybe maxlen=16
director Text Maybe maxlen=128
actors Text Maybe maxlen=512
description Text Maybe maxlen=512
path Text Maybe maxlen=128
codec Text Maybe maxlen=32
length Int Maybe maxlen=11
poster Text Maybe maxlen=128
added UTCTime default=CURRENT_TIMESTAMP
Primary title year
deriving Show
|]
main :: IO ()
main = do
(path:args) <- getArgs
movies <- runSqlite (pack path) $ do
runMigration migrateAll
selectList [] [Desc MovieTitle]
mapM_ print movies
This is sort of a contrived example, but it illustrates my point. I would like to have a composite primary key that consists of title and year. Everything compiles fine and the table is created with the schema that I need. When I try to use the Movie type as defined above in my yesod application with persistent-sqlite, I get the following compile-time error
Foundation.hs:35:1:
No instance for (PathPiece MovieId)
arising from a use of ‘toPathPiece’
In the first argument of ‘(:)’, namely ‘(toPathPiece dyn_apvA)’
In the second argument of ‘(:)’, namely
‘((toPathPiece dyn_apvA) : [])’
In the expression:
((Data.Text.pack "entry") : ((toPathPiece dyn_apvA) : []))
Foundation.hs:35:1:
No instance for (PathPiece MovieId)
arising from a use of ‘fromPathPiece’
In the expression: fromPathPiece
In the pattern: fromPathPiece -> Just dyn_apwt
In the pattern: (:) (fromPathPiece -> Just dyn_apwt) []
I am stuck at this point and unable to continue on my own. I have tried searching, but I am only able to find a working example of composite primary keys without the use of yesod. I have a feeling that it should be possible to do since the composite primary key using only persistent-sqlite works.
Here is how the Movie type is defined in config/models
Movie
title Text maxlen=20
year Int maxlen=11
genre Text Maybe maxlen=128
mpaa Text Maybe maxlen=16
director Text Maybe maxlen=128
actors Text Maybe maxlen=512
description Text Maybe maxlen=512
path Text Maybe maxlen=128
codec Text Maybe maxlen=32
length Int Maybe maxlen=11
poster Text Maybe maxlen=128
added UTCTime default=CURRENT_TIMESTAMP
Primary title year
deriving

I haven't actually used the composite primary key feature myself, but this error makes sense to me. There's not obvious serialization to Text for arbitrary composite keys, and so persistent doesn't generate it for you. If you'd like to use a MovieId inside your URLs, you'll need to manually define the PathPiece instance, which is just a pair of functions for converting to and from Text.

Related

Is there a way to generate a dhall schema from an haskell datatype?

I'm trying to rewrite a BIG yaml configuration file used in Haksell applicationusing dhall.
To do so I'm using json-to-dhall which requires a SCHEMA which is the type of the resuting expression. The problem is the actual schema is almost impossible to write manually as involves lots of sum types (and nested sum types). I tried to generate the schema by converting manually some parts of the yaml to dhall, and run dhall type. This give a schema that I can use with jston-to-dhall later. This works for simple types, but now I'm facing the problem with unions (of unions). Dhall needs type annotations to write the file I'm using to generate the type ... So I was wondering is there a way (either using a tool or modifying my haskell application) to either dump an Haskell data to a correct dhall file or at least generate the schema from the Haskell type.
Yes, you can generate the Dhall type from the Haskell type.
Here is an example of how to do so:
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE TypeApplications #-}
import Data.Either.Validation (Validation(..))
import Data.Text (Text)
import Dhall (FromDhall)
import GHC.Generics (Generic)
import Numeric.Natural (Natural)
import qualified Data.Text.IO as Text.IO
import qualified Dhall
import qualified Dhall.Core
data Mood = Happy | Sad
deriving (Generic, FromDhall)
data Person = Person { age :: Natural, name :: Text, mood :: Mood }
deriving (Generic, FromDhall)
main :: IO ()
main = do
case Dhall.expected (Dhall.auto #Person) of
Success result -> Text.IO.putStrLn (Dhall.Core.pretty result)
Failure errors -> print errors
... which outputs:
$ runghc ./example.hs
{ age : Natural, name : Text, mood : < Happy | Sad > }

See implicit exports from a Module

Quite surprisingly, I'm trying to fix a compilation warning missing-export-lists by adding explicitly the elements imported one by one for a specific module, but somehow there is something magical that this module is implicitly exporting that I can't find....
is it possible to retrieve what is exported implicitly with ghc ?
Here is an example of my issue, Yesod is generating some code with TH and quasiquotes :
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
import Control.Monad.IO.Class (liftIO)
import Database.Persist
import Database.Persist.Sqlite
import Database.Persist.TH
share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
Person
name String
age Int Maybe
deriving Show
|]
Then let's say we use that generated code that way :
people <- selectList
( [PersonAge >. 25, PersonAge <=. 30]
||. [PersonName /<-. ["Adam", "Bonny"]]
||. ([PersonAge ==. 50] ||. [PersonAge ==. 60])
)
[]
liftIO $ print people
Now I want to put share in a specific module and control precisely what I'm exporting from that code generated.
I don't know how it generates PersonName and PersonAge ! I don't how to specifically import these 2 types for example...

deriving Generic doesn't work even though DeriveGeneric is on

I'm trying to follow the tutorial for the Beam Haskell library: https://tathougies.github.io/beam/tutorials/tutorial1/
module Lib
( someFunc
) where
{-# LANGUAGE
DeriveGeneric
, GADTs
, OverloadedStrings
, FlexibleContexts
, FlexibleInstances
, TypeFamilies
, TypeApplications
#-}
import Database.Beam
import Database.Beam.Postgres
import GHC.Generics
import Data.Text (Text)
data UserT f
= User
{ _userEmail :: Columnar f Text
, _userFirstName :: Columnar f Text
, _userLastName :: Columnar f Text
, _userPassword :: Columnar f Text }
deriving Generic
someFunc :: IO ()
someFunc = putStrLn "someFunc"
This results in the following error:
• Can't make a derived instance of ‘Generic (UserT f)’:
You need DeriveGeneric to derive an instance for this class
• In the data declaration for ‘UserT’
|
27 | deriving Generic
| ^^^^^^^
Note that the DeriveGeneric language pragma is present.
What am I missing here?
Build environment:
stack lts-11.9
Linux
A {-# LANGUAGE #-} declaration needs to go at the very top of the file, before the module declaration.

Coercing types in Persistent

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?
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");
For posterity: I recently struggled with The type variable ‘backend0’ is ambiguous for a toy Sqlite example. The complier (rightly) couldn't figure out which backend I wanted to use for which BaseBackend backend = SqlBackend.
Turns out there are three of these: SqlBackend, SqlWriteBacknde and SqlReadBackend. Yes, for the first one, the resolved associated type happens to resolve to itself, if you wonder if it's a typo.
You can either fix by putting an explicit type signature somewhere (stick a type hole :: _ on an operation to get a hint), or by including the runMigration call in your operations, which magically fixes the type variable to SqlBackend.

How do you add a unique key to a Database.Persist model using the quasiquoter?

How do you build a unique key with two or more fields using the persistLowerCase quasiquoter?
When using Database.Persist to create models for a simple website using guidance from the Yesod book, the following error crops up:
Build FAILED
$PREFIX/App/Models.hs: line 42, column 18:
Not in scope: data constructor `GroupSlug'
$PREFIX/App/Models.hs: line 43, column 19:
Not in scope: data constructor `GroupName'
App/Models.hs
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module App.Models where
import Data.Text (Text)
import Database.Persist.TH
share [ mkPersist sqlSettings
, mkMigrate "migrateAll"] [persistLowerCase|
Post
group GroupId
postName Text
postSlug Text
period Text
content_en Text
content_pt Text
UniquePost postSlug
deriving Show
Group
groupId Int
groupName Text
groupSlug Text
parent GroupId
UniqueGroup groupId groupSlug
deriving Show
|]
App/Handlers.hs
{-# LANGUAGE OverloadedStrings #-}
module App.Handlers where
import Database.Esqueleto
import App.Models
groupQuery x =
select $
from $ \g -> do
where_ (g ^. GroupSlug ==. val x)
return $ g ^. GroupName
Versions:
Haskell Platform 2013.2
Cabal == 1.19.2
persistent == 1.3.0.2
scotty == 0.6.2
esqueleto == 1.3.4.5
Am I missing some extension or overlooking something else?
As far as I'm aware, data constructors that you're trying to use are actually called GroupGroupSlug and GroupGroupName (not just GroupSlug and GroupName). That is because the data type is generated by taking the entity name and joining it with the field name (with proper capitalisation). Try loading your source files with the -ddump-splices command-line parameter, and you should see it in the output somewhere.

Resources