16

Io中,您可以使用以下命令设置执行上下文do

Http := Object clone
Http get := method(uri, ("<GET request to " .. uri .. ">") println)
Http delete := method(uri, ("<DELETE request to " .. uri .. ">") println)

Database := Object clone
Database insert := method(table, data, ("<insert data to " .. table .. ">") println)
Database delete := method(table, id, ("<delete " .. id .. " from " .. table .. ">") println)

Http do(
  get("http://example.com/")
  delete("http://example.com/something")
)

Database do(
  insert("cats", list("Phil", "gray"))
  delete("cats", 12)
)

(Ruby 具有与 类似的特性Object#instance_exec,但它的对象模型要复杂一些。)

实际上,这为您提供了一个临时命名空间,非常适合编写特定领域的语言。有没有一种技术可以在 Haskell 中实现类似的效果(临时命名空间)?

例如,类似:(不一定完全像这样,但具有类似简洁语法的东西。)

main = do
  http $ do
    get "http://example.com/"
    delete "http://example.com/something"
  database $ do
    insert "cats" ["Phil", "gray"]
    delete "cats" 12

请注意,这两个deletes 是完全不同的功能。我宁愿避免写诸如H.deleteand之类的东西D.delete,因为那样会很快变得一团糟。我意识到可以通过将数据库版本重命名为,例如,来避免这种情况deleteFrom,但我不想这样做。

4

3 回答 3

16

这是RecordWildCards最近在一系列非常有趣的帖子中讨论得很多的方法,例如在reddit/r/haskell上的这篇文章和在这里剖析的“模块化前奏曲”repo,最后在此处讨论了本教程帖子

在使用该方法的一种方式中,我们需要两个辅助模块(这些只是模仿您想要的):

module HTTP where
import System.Directory

data Http = Http {get :: String -> IO String, delete :: String -> IO ()}

http :: Http
http = Http {get = fmap reverse . readFile, delete = removeFile}

module DB where
import System.Directory

data DB = Db {get :: String -> IO String, insert :: String -> String -> IO ()}

db :: DB
db = Db readFile appendFile

然后导入它们,RecordWildCards用于实现类似于您的计划的内容:

{-#LANGUAGE RecordWildCards#-}
import           DB   -- qualified imports might be better
import           HTTP     -- but aren't needed in this case

main =  do 
  let Http{..} = http
  do test <- get "test.txt"
     delete "test2.txt"
     putStrLn $ take 10 test

  let Db{..} = db
  do test3 <- get "test3.txt"
     insert "test4.txt" "happy" 
     putStrLn $ take 10 test3 
     get "test4.txt" >>= putStrLn

这比方法不那么丑陋,但ImplicitParams比 Daniel Wagner 的方法更灵活,否则是最帅的。

于 2012-07-26T05:08:34.530 回答
16

“那是疯狂的动态东西。你永远无法用静态语言做到这一点......”

{-# LANGUAGE ImplicitParams, Rank2Types #-}
import Text.Printf
http :: (((?get :: String -> IO ()),
          (?delete :: String -> IO ())) 
         => IO a)
        -> IO a
http a = let ?get    = \s -> printf "http get %s\n" s
             ?delete = \s -> printf "http delete %s\n" s
         in a

database :: (((?insert :: String -> [String] -> IO ()),
              (?delete :: String -> Integer -> IO ())) 
               => IO a) 
            -> IO a
database a = let ?insert = \s ls -> printf "database insert %s %s\n" s (show ls)
                 ?delete = \s n  -> printf "database delete %s %d\n" s n
             in a

main = do
  http $ do
    ?get "http://example.com/"
    ?delete "http://example.com/something"
  database $ do
    ?insert "cats" ["Phil", "gray"]
    ?delete "cats" 12

“这太疯狂了。不可能”

*Main> :l Crazy.hs 
[1 of 1] Compiling Main             ( Crazy.hs, interpreted )
Ok, modules loaded: Main.
*Main> main
http get http://example.com/
http delete http://example.com/something
database insert cats ["Phil","gray"]
database delete cats 12

您可能实际上不应该以这种方式做事。但如果这真的是你想要的,隐式参数可能是最优雅的获取方式


applicative 的 RecordWildCard 解决方案在某种意义上比这更好,因为它需要更少的代码来设置,并且涉及的语言扩展要小得多。此外,它非常类似于在具有适当模块系统的语言中使用“open”指令,或在具有灵活名称间距的语言中使用“使用命名空间”命令(Haskell 两者都没有)。隐式参数解决方案捕获类似于动态语言语义的东西。特别是,使用它,您可以调用使用隐式参数的函数,而不必担心手动传递环境。

您还可以在此处使用 ReaderT 或类似的东西模拟隐式参数。虽然(由于各种原因),如果你想留在 Haskell 98 中,那不是很有组合性。

于 2012-07-26T04:09:35.523 回答
15

只是为了好玩,这里是 Philip JF 的答案的翻译,没有使用任何疯狂的扩展废话。事实上,这很无聊,一旦你弄清楚了如何去做:

import Text.Printf

http :: ((String -> IO ()) -> (String -> IO ()) -> IO a)
     -> IO a
http a = a (printf "http get %s\n") (printf "http delete %s\n")

database :: ((String -> [String] -> IO ()) -> (String -> Integer -> IO ()) -> IO a) 
         -> IO a
database a = a (\s -> printf "database insert %s %s\n" s . show)
               (printf "database delete %s %d\n")

main = do
  http $ \get delete -> do
    get "http://example.com/"
    delete "http://example.com/something"
  database $ \insert delete -> do
    insert "cats" ["Phil", "gray"]
    delete "cats" 12
于 2012-07-26T04:38:21.837 回答