将monad线组合成更通用的东西

时间:2012-07-19 17:10:58

标签: haskell

我正在编写一些mysql简单的数据库代码,我正在使我的数据类型成为QueryResults类型类的成员,以便轻松取消它们。

但我最终会遇到很多功能:

data FullName {
  first_name :: String,
  middle_name :: String,
  last_name :: String
} deriving Show

newtype UID = UID Integer deriving Show

go :: [(String, String)] -> Maybe FullName
go fvs = do
  first  <- lookup "first_name" fvs
  middle <- lookup "middle_name" fvs
  last   <- lookup "last_name" fvs
  return $ FullName first middle last

go :: [(String, String)] -> Maybe UID
go fvs = do
  uid <- lookup "uid" fvs
  return $ UID (read uid)

其中lookup只返回(可能是a)。其中一些数据类型中包含十几列,因此这很乏味。

所以有很多这样的数据类型,我希望能够编写一个我会这样调用的函数:

go RealName ["first_name","middle_name","last_name"] fvs
go UID ["uid"] fvs

但是我不知道这种事情应该是什么类型,或者我将如何去做。也许这甚至不可能。

3 个答案:

答案 0 :(得分:3)

使用Template Haskell,我怀疑你最终会得到这样的代码:

makeGo ''FullName

扩展为

goFullName :: [(String, String)] -> Maybe FullName
goFullName fvs = do
  first  <- lookup "first_name" fvs
  middle <- lookup "middle_name" fvs
  last   <- lookup "last_name" fvs
  return $ FullName first middle last

撇开:如果你import Control.ApplicativegoFullName可以写得更简洁,就像这样:

goFullName :: [(String, String)] -> Maybe FullName
goFullName fvs
   = FullName <$> lookup "first_name" fvs
              <*> lookup "middle_name" fvs
              <*> lookup "last_name" fvs

答案 1 :(得分:2)

我会把这个函数写成

import Control.Applicative

go :: [(String, String)] -> Maybe FullName
go fvs = FullName <$> l "first_name" <*> l "middle_name" <*> l "last_name"
    where l field = lookup field fvs

答案 2 :(得分:0)

如果您的目标是最终使用QueryResults个实例,那么为什么不执行以下操作:

{-# LANGUAGE GeneralizedNewtypeDeriving,OverloadedStrings #-}

Module Main where

import Database.MySQL.Simple
import Database.MySQL.Simple.QueryResults
import Database.MySQL.Simple.Result
import Database.MySQL.Simple.Param

data FullName =
  FullName {
       first_name  :: String
     , middle_name :: String
     , last_name   :: String
  } deriving Show

newtype UID = UID Integer deriving (Show,Param)

instance QueryResults FullName where
  convertResults fs@[_,_,_] vs@[_,_,_] = FullName first middle last
    where [first,middle,last] = zipWith convert fs vs
  convertResults fs vs  = convertError fs vs 3

instance QueryResults UID where
  convertResults fs@[fuid] vs@[vuid] = UID (convert fuid vuid)
  convertResults fs vs  = convertError fs vs 1

uidToFullName :: UID -> IO FullName
uidToFullName (UID uid) =
  do conn <- connect defaultConnectInfo
     [fn] <- query conn
         "SELECT first_name,middle_name,last_name FROM user WHERE uid = ?" [uid]
     return fn

示例会话:

λ> :load Main.sh
λ> uidToFullName (UID 23)
FullName {first_name = "John", middle_name = "Horatio", last_name = "Smith"}

以下是随附的SQL:

CREATE TABLE user (
  uid Integer PRIMARY KEY,
  email Text,
  first_name Text,
  middle_name Text,
  last_name Text
);

INSERT INTO user VALUES (23,'john.horation@smith.com','John','Horatio','Smith');