假设我们有一个模块负责向 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")
答案 0 :(得分:6)
使用reader monad,您可以隐藏常见的环境'你所有的功能(API密钥)。这是一个简单的例子:
首先,
import Control.Monad.Reader
然后,一些类型别名以帮助提高可读性。这里值得注意的是FlickrRequest a
部分 - 它表示一个flickr请求,它返回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
阅读器隐式传递的。在这些功能中,您可以使用ask
访问该环境(您可以阅读&#39;环境)。当组合所有在相同环境中运行的功能时,例如:
-- 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)
答案 1 :(得分:4)
Frerich Raabe的解决方案非常适合Haskell,但遗憾的是我们没有Elm中的do
符号或者读者Monad的等价物。
但是,我们有端口,我们可以使用这些端口在从Javascript初始化Elm模块时提供配置数据。
例如,您可以在Elm中定义一个名为apiKey
的端口。由于端口的值来自javascript,我们只定义函数签名,而不是body:
port apiKey : String
在启动Elm模块的HTML / javascript文件中,您可以传递包含初始端口值的第二个参数,如下所示:
<script>
var app = Elm.fullscreen(Elm.Main, {
apiKey: "myApiKey"
});
</script>
现在,在整个Elm代码中,您有一个始终可用的名为apiKey
的常量函数。您永远不需要将其作为参数传递给其他函数。
答案 2 :(得分:0)
仅仅是为了解释我的评论,我的意思是将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"