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

delete2つは完全に異なる機能であることに注意してください。H.deleteやのようなものを書くのは避けたいと思いD.deleteます。そうするとすぐに面倒になるからです。そのデータベースバージョンの名前をたとえばに変更することでこれを回避できることはわかっていますdeleteFromが、そうしたくありません。

4

3 に答える 3

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」ディレクティブを使用したり、名前空間が柔軟な言語で「namespace」コマンドを使用したりするのと非常によく似ています(Haskellにはどちらもありません)。暗黙のパラメーターソリューションは、動的言語のセマンティクスに似たものをキャプチャします。特に、これを使用すると、暗黙の引数を使用する関数を呼び出すことができ、手動で環境を渡すことを心配する必要はありません。

ReaderTなどを使用して、ここで暗黙的なパラメーターをシミュレートすることもできます。(さまざまな理由で)とはいえ、Haskell 98にとどまりたいのであれば、それはあまり構成的ではありません。

于 2012-07-26T04:09:35.523 に答える
16

RecordWildCardsこれは、非常に興味深い投稿の急増で最近議論された方法です。たとえば、 reddit / r / haskellに関するこの投稿と、ここで分析された「モジュラープレリュード」リポジトリ、そして最後にここで議論されたこのチュートリアル投稿です。

この方法を使用する1つの方法では、2つのヘルパーモジュールが必要になります(これらは意図したものを模倣するだけです)。

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他の点では最もハンサムなダニエル・ワーグナーのアプローチよりも柔軟性があります。

于 2012-07-26T05:08:34.530 に答える
15

楽しみのために、これは、クレイジーな拡張機能のナンセンスを使用しない、PhilipJFの回答の翻訳です。実際、それを行う方法を理解したら、それは非常に退屈です。

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 に答える