Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Temporary namespace/context in Haskell

In Io, you can set the execution context using 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 has a similar feature with Object#instance_exec, but its object model is a bit more complicated.)

In effect, this gives you a temporary namespace, which is nice for writing domain specific languages. Is there a technique to achieve a similar effect (a temporary namespace) in Haskell?

For example, something like: (Not necessarily exactly like this, but something with similarly succinct syntax.)

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

Notice that the two deletes are completely different functions. I'd prefer to avoid writing things like H.delete and D.delete, because that would get messy quick. I realize that this could be avoided by renaming that database version to, for example, deleteFrom, but I don't want to.

like image 205
Snowball Avatar asked Jul 26 '12 03:07

Snowball


1 Answers

"That is crazy dynamic stuff. You could never do that in a static language..."

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

"that is crazy. No way it works"

*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

you should probably not actually do things this way. But if that is really what you want, implicit parameters are probably the most elegant way to get it


applicative's RecordWildCard solution is in some sense better than this since it takes less code to set up, and involves a much smaller extension to the language. Furthermore it is very similar to using "open" directives in languages with proper module systems, or "using namespace" commands in languages with flexible name spacing (Haskell has neither). The implicit parameter solution captures something similar to the semantics of dynamic languages. In particular, using it you can call a function that uses the implicit arguments, and not have to worry about manually passing the environment.

You can also simulate implicit parameters here with a ReaderT or something like it. Although (for various reasons), that is not very compositional if you want to stay in Haskell 98.

like image 122
Philip JF Avatar answered Sep 30 '22 17:09

Philip JF