使用一组默认值初始化模块(elm 或 haskell)
Initializing a module with set of defaults (elm or haskell)
假设我们有一个模块负责向 Flickr 发出 api 请求。我不想将 api 键硬编码到 Flickr 模块中。 Api 密钥可以通过 ajax 请求获得。
目前 Flickr 模块中的每个函数都接受 apiKey 作为其参数。但是,将 api 键传递给周围并不是那么酷。有什么简单的方法可以解决吗?或者是否可以在不传递每个函数的情况下在模块之间共享一些值。
module Flickr where
searchPhotos : String -> String -> ...
searchPhotos apiKey query = ...
getPhotoInfo : String -> String -> ...
getPhotoInfo apiKey photoId = ...
anotherOne : String -> ...
anotherOne apiKey = ...
更新: 到目前为止我尝试的是部分应用函数。我把 apiKey 之类的参数放在最后。但是现在我必须把这个功能传递给周围,还有其他想法吗?
makeFlickrRequest : (String -> String -> a) -> a
makeFlickrRequest flickrMethod = flickrMethod "myApikey" "mySecret"
photosSearch : String -> String -> String -> ...
photosSearch query apiKey secret =
makeHTTPCallhere ...
-- Usage:
makeFlickrRequest (photosSearch "haskell")
使用 reader monad,您可以隐藏所有功能的公共 'environment'(API 键)。这是一个简单的例子:
首先,
import Control.Monad.Reader
然后,一些类型别名有助于提高可读性。这里值得注意的是 FlickrRequest a
部分 - 它代表一个 flickr 请求,其中 returns 类型为 a
:
的值
type APIKey = String
type Photo = String
type PhotoInfo = String
type FlickrRequest a = Reader APIKey a
这是搜索照片和获取某些照片信息的两个虚拟实现:
searchPhotos :: String -> FlickrRequest [Photo]
searchPhotos query = do
apiKey <- ask
return ["<Photo for query " ++ query ++ " (api key " ++ apiKey ++ ")>"]
getPhotoInfo :: Photo -> FlickrRequest PhotoInfo
getPhotoInfo photo = do
apiKey <- ask
return $ "This is the photo information for photo " ++ photo ++ " (" ++ apiKey ++ ")"
请注意,API 键是通过 FlickrRequest
reader 隐式传递的。在函数中,您可以使用 ask
访问该环境(您获得 'read' 环境)。当组合所有在同一环境中运行的功能时,它的美妙之处就体现出来了,例如:
-- This could be just `searchPhotos "*" >>= mapM getPhotoInfo` but I don't
-- want to obscure things unnecessarily.
allPhotoInfos :: FlickrRequest [PhotoInfo]
allPhotoInfos = do
photos <- searchPhotos "*"
sequence (map getPhotoInfo photos)
我们首先调用 searchPhotos
,然后将 getPhotoInfo
应用于所有找到的照片。请注意 API 密钥是如何无处可见的,它是隐式传递的!
最后,要运行整个事情,可以使用runReader
功能。像
main :: IO ()
main = do
let myAPIKey = "someAPIKey"
print (runReader allPhotoInfos myAPIKey)
只是为了解释我的评论,我的意思是将 apikey 应用于每个函数,如下所示 example:
type ApiKey = String
apikey::ApiKey
apikey = "foo"
f1 :: ApiKey -> String -> String
f1 "foo" _ = "1"
f1 _ s = s
f2 :: ApiKey -> String -> String
f2 "foo" _ = "2"
f2 _ s = s
f1', f2':: (String -> String)
[f1', f2'] = map (\x-> x apikey) [f1, f2]
main = do
putStrLn $ f1 "asdf" "2"
putStrLn $ f1' "2"
Frerich Raabe 的解决方案非常适合 Haskell,但不幸的是,我们没有 Elm 中的 do
符号或 Reader Monad 等价物。
但是,我们有端口,我们可以使用这些端口在从 Javascript 初始化 Elm 模块时提供配置数据。
例如,您可以在 Elm 中定义一个名为 apiKey
的端口。由于端口的值来自javascript,我们只定义函数签名,不定义函数体:
port apiKey : String
在启动 Elm 模块的 HTML/javascript 文件中,您可以传递第二个包含初始端口值的参数,如下所示:
<script>
var app = Elm.fullscreen(Elm.Main, {
apiKey: "myApiKey"
});
</script>
现在,在您的整个 Elm 代码中,您有一个名为 apiKey
的常量函数始终可用。您永远不需要将它作为参数传递给其他函数。
假设我们有一个模块负责向 Flickr 发出 api 请求。我不想将 api 键硬编码到 Flickr 模块中。 Api 密钥可以通过 ajax 请求获得。
目前 Flickr 模块中的每个函数都接受 apiKey 作为其参数。但是,将 api 键传递给周围并不是那么酷。有什么简单的方法可以解决吗?或者是否可以在不传递每个函数的情况下在模块之间共享一些值。
module Flickr where
searchPhotos : String -> String -> ...
searchPhotos apiKey query = ...
getPhotoInfo : String -> String -> ...
getPhotoInfo apiKey photoId = ...
anotherOne : String -> ...
anotherOne apiKey = ...
更新: 到目前为止我尝试的是部分应用函数。我把 apiKey 之类的参数放在最后。但是现在我必须把这个功能传递给周围,还有其他想法吗?
makeFlickrRequest : (String -> String -> a) -> a
makeFlickrRequest flickrMethod = flickrMethod "myApikey" "mySecret"
photosSearch : String -> String -> String -> ...
photosSearch query apiKey secret =
makeHTTPCallhere ...
-- Usage:
makeFlickrRequest (photosSearch "haskell")
使用 reader monad,您可以隐藏所有功能的公共 'environment'(API 键)。这是一个简单的例子:
首先,
import Control.Monad.Reader
然后,一些类型别名有助于提高可读性。这里值得注意的是 FlickrRequest a
部分 - 它代表一个 flickr 请求,其中 returns 类型为 a
:
type APIKey = String
type Photo = String
type PhotoInfo = String
type FlickrRequest a = Reader APIKey a
这是搜索照片和获取某些照片信息的两个虚拟实现:
searchPhotos :: String -> FlickrRequest [Photo]
searchPhotos query = do
apiKey <- ask
return ["<Photo for query " ++ query ++ " (api key " ++ apiKey ++ ")>"]
getPhotoInfo :: Photo -> FlickrRequest PhotoInfo
getPhotoInfo photo = do
apiKey <- ask
return $ "This is the photo information for photo " ++ photo ++ " (" ++ apiKey ++ ")"
请注意,API 键是通过 FlickrRequest
reader 隐式传递的。在函数中,您可以使用 ask
访问该环境(您获得 'read' 环境)。当组合所有在同一环境中运行的功能时,它的美妙之处就体现出来了,例如:
-- This could be just `searchPhotos "*" >>= mapM getPhotoInfo` but I don't
-- want to obscure things unnecessarily.
allPhotoInfos :: FlickrRequest [PhotoInfo]
allPhotoInfos = do
photos <- searchPhotos "*"
sequence (map getPhotoInfo photos)
我们首先调用 searchPhotos
,然后将 getPhotoInfo
应用于所有找到的照片。请注意 API 密钥是如何无处可见的,它是隐式传递的!
最后,要运行整个事情,可以使用runReader
功能。像
main :: IO ()
main = do
let myAPIKey = "someAPIKey"
print (runReader allPhotoInfos myAPIKey)
只是为了解释我的评论,我的意思是将 apikey 应用于每个函数,如下所示 example:
type ApiKey = String
apikey::ApiKey
apikey = "foo"
f1 :: ApiKey -> String -> String
f1 "foo" _ = "1"
f1 _ s = s
f2 :: ApiKey -> String -> String
f2 "foo" _ = "2"
f2 _ s = s
f1', f2':: (String -> String)
[f1', f2'] = map (\x-> x apikey) [f1, f2]
main = do
putStrLn $ f1 "asdf" "2"
putStrLn $ f1' "2"
Frerich Raabe 的解决方案非常适合 Haskell,但不幸的是,我们没有 Elm 中的 do
符号或 Reader Monad 等价物。
但是,我们有端口,我们可以使用这些端口在从 Javascript 初始化 Elm 模块时提供配置数据。
例如,您可以在 Elm 中定义一个名为 apiKey
的端口。由于端口的值来自javascript,我们只定义函数签名,不定义函数体:
port apiKey : String
在启动 Elm 模块的 HTML/javascript 文件中,您可以传递第二个包含初始端口值的参数,如下所示:
<script>
var app = Elm.fullscreen(Elm.Main, {
apiKey: "myApiKey"
});
</script>
现在,在您的整个 Elm 代码中,您有一个名为 apiKey
的常量函数始终可用。您永远不需要将它作为参数传递给其他函数。