在Persistent中强制类型

时间:2014-12-01 00:13:07

标签: haskell yesod

这是一个当前失败的“hello world”尝试 - 我只是尝试使用以下代码在SqLite数据库上运行selectList查询:

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

这几乎看起来太简单了搞砸......我哪里出错了?

2 个答案:

答案 0 :(得分:6)

正如您可能已经知道的那样,Haskell的优势之一就是强力打字。持久性sqlite包通过要求表条目具有自己的数据类型,将其变为极端(在我看来,这是一件好事)。

例如,如果你有一个表格可以保存看起来像这样的水果

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

并使用persistent-sqlite对此表进行查询,结果应存储在相应的Fruit类型中

data Fruit = Fruit { fruitName::String }

仅仅创建所述数据类型还不够,有一堆样板代码可以创建所需的类实例来实际使用它。您可以在持久模板库中使用模板Haskell magic来为您创建所有这些,而不是手动创建。

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

您的示例代码实际上是正确的,但缺少所有这些东西。此外,编译器甚至不知道尝试使用什么类型,因此,您收到的错误消息包含以下句子

The type variable ‘val0’ is ambiguous

这基本上就是编译器的说法,&#34;我不知道提取sql条目的类型。&#34;您可以使用显式类型指定

print (fruits :: [Entity Fruit])

最后,不幸的是,这段代码使用了一堆GHC扩展。将这些放在一起,这是一个更完整的最简单的例子。

{-# 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])

并且只是为了完成,这里是如何填充sqlite db来测试它。

> sqlite3 fruitDb.sqlite

sqlite> create table fruit (id, name);
sqlite> insert into fruit values (0, "apple");
sqlite> insert into fruit values (1, "orange");

答案 1 :(得分:3)

对于后人:我最近与The type variable ‘backend0’ is ambiguous争论玩具Sqlite示例。编译器(正确地)无法确定我想用哪个backend BaseBackend backend = SqlBackend

原来有三个:SqlBackendSqlWriteBackndeSqlReadBackend。是的,对于第一个,如果你想知道它是否是一个错字,那么已解决的关联类型会自行解决。

你可以通过在某个地方放置一个显式类型签名来修复(在操作上粘贴一个类型的洞:: _来获得提示),或者在你的操作中包含runMigration调用,这会神奇地修复类型变量为SqlBackend