如何在 IHP 脚本中重用 IHP 类?

How to reuse IHP classes in a IHP script?

我使用 IHP(haskell 网络框架)创建了一个网络应用程序。现在我想创建一个 IHP 脚本来将一些外部数据加载到我的数据库中。然而,我从 Prelude 中得到了很多导入冲突,但不是我期望的类型。

#!/usr/bin/env run-script
module Application.Script.DataLoader where

import Application.Script.Prelude      hiding (decode, pack, (.:))
import qualified Data.ByteString.Lazy  as     BL
import Data.Csv
import Data.Text                              (pack)
import qualified Data.Vector           as     V
import Control.Monad                          (mzero)

instance FromNamedRecord Product where
    parseNamedRecord r = Product def <$> r .: "title" <*> r .: "price" <*> r .: "category" <*> pure def

run :: Script
run = do
    csvData <- BL.readFile "~/tender/data/Boiler-en-kookkraan_Boiler.csv"
    case decodeByName csvData of
        Left err -> putStrLn $ pack err
        Right (_, v) -> V.forM_ v $ \ p ->
            putStrLn $ (get #title p) ++ ", " ++ show (get #price p) ++ " euro"

我的 Product 架构如下所示:

CREATE TABLE products (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    title TEXT NOT NULL,
    price DOUBLE PRECISION NOT NULL,
    category TEXT NOT NULL
);

有没有一种方法可以将我创建的类型用作数据对象,例如读取我的 csv 到?

[更新输出]

Application/Script/DataLoader.hs:12:26: error:
    • Couldn't match type ‘MetaBag -> Product' a1’
                     with ‘Product' (QueryBuilder ProjectProduct)’
      Expected type: Parser Product
        Actual type: Parser (MetaBag -> Product' a1)
    • In the expression:
        Product def <$> r .: "title" <*> r .: "price" <*> r .: "category"
          <*> pure def
      In an equation for ‘parseNamedRecord’:
          parseNamedRecord r
            = Product def <$> r .: "title" <*> r .: "price" <*> r .: "category"
                <*> pure def
      In the instance declaration for ‘FromNamedRecord Product’
   |
12 |     parseNamedRecord r = Product def <$> r .: "title" <*> r .: "price" <*> r .: "category" <*> pure def
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

[已解决,感谢@mpscholten 的帮助]

#!/usr/bin/env run-script
module Application.Script.DataLoader where

import Application.Script.Prelude      hiding (decode, pack, (.:))
import qualified Data.ByteString.Lazy  as     BL
import Data.Csv
import Data.Text                              (pack)
import qualified Data.Vector           as     V
import Control.Monad                          (mzero)

parseProduct :: NamedRecord -> Parser Product
parseProduct r = do
  title <- r .: "title"
  price <- r .: "price"
  category <- r .: "category"

  newRecord @Product
    |> set #title title
    |> set #price price
    |> set #category category
    |> pure

run :: Script
run = do
    csvData <- BL.readFile "data/Boiler-en-kookkraan_Boiler.csv"
    case decodeByNameWithP parseProduct defaultDecodeOptions csvData of
        Left err -> putStrLn $ pack err
        Right (_, v) -> V.forM_ v $ \ p ->
            putStrLn $ (get #title p) ++ ", " ++ show (get #price p) ++ " euro"

您能分享一下您遇到的错误吗?您需要的产品类型应该在 Generated.Types 中,哪个 intern 由 Application.Script.Prelude.

加载

我认为您可能有两个模型都具有字段标题。 haskell 中的字段是函数,它们不能被使用两次。

FromNamedRecord 实例中,您缺少两个字段:idmetaid 字段是记录的第一个字段。 meta 字段是 IHP 用于跟踪验证错误的隐藏字段。它始终是记录的最后一个字段。

解决这个问题的最简单方法是使用 newRecord 并以更明确的方式写出代码:

instance FromNamedRecord Product where
    parseNamedRecord r = do
        title <- r .: "title"
        price <- r .: "price"
        category <- r .: "category"

        newRecord @Product
            |> set #title title
            |> set #price price
            |> set #category category
            |> pure

对于错误“出现歧义‘标题’”,尝试使用 get 函数而不是使用正常的 haskell 访问器函数:

putStrLn $ (get #title p) ++ ", " ++ show (get #price p) ++ " euro"