使用一组默认值(elm或haskell)初始化模块

时间:2016-01-08 11:25:06

标签: haskell elm

假设我们有一个模块负责向 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")

3 个答案:

答案 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"