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 delete
s 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.
"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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With