如何在Elm(或Haskell)中的函数之间共享数据

如何在Elm(或Haskell)中的函数之间共享数据,haskell,functional-programming,elm,Haskell,Functional Programming,Elm,我想在elm中为外部api创建一个http客户端。在scala(即OO/FP混合)中,我将其简单地表示为(暂时忘记异步): class Client(url: String) { def getFoo(): String = ??? } 但在埃尔姆,我有点迷路了。显而易见的解决方案是将url直接传递给函数 module Client getFoo : String -> String 但是使用它非常痛苦,因为它使每个调用复杂化,因此随着定义的函数数量和对这些函数的调用数量的增

我想在elm中为外部api创建一个http客户端。在scala(即OO/FP混合)中,我将其简单地表示为(暂时忘记异步):

class Client(url: String) {
    def getFoo(): String = ???
}
但在埃尔姆,我有点迷路了。显而易见的解决方案是将url直接传递给函数

module Client

getFoo : String -> String
但是使用它非常痛苦,因为它使每个调用复杂化,因此随着定义的函数数量和对这些函数的调用数量的增加,负担也会增加

我尝试使用具有以下功能的记录:

type alias Client = { getFoo: String }

createClient : String -> Client
但这感觉像是对OOP的拙劣模仿。AFAIU这是通过Ocaml中的函子和OOP中的对象解决的


在Elm(或者Haskell,如果Elm在这里缺少某些特定功能)中,什么是实现这一点的标准方法?

请记住,OO方法调用†只不过是为函数提供一个额外的
/
参数的语法糖:

--  OO                       ┃      functional/procedural
Client c = ...;              │     c = ... :: Client
...                          │     ...
main() {print(c.getFoo());}  │     main = print(getFoo c)
因此,无论是在像C这样的过程语言中还是在FP语言中,走这条路都是非常可能的,而且通常是有用的

data Client {
    url :: String
  , ...
  }

getFoo :: Client -> String
getFoo (Client{url = u}) = ...
是的,这要求您显式地传递
客户机
对象,但这并不一定是件坏事——只要您有正确区分的类型,就可以很明显地知道需要传递什么作为什么函数的哪个参数,这种方法实际上比OO方法更具可伸缩性,因为您可以将多个对象作为参数,并且每个函数可以只接受它需要的对象

当然,在某些情况下,您确实有一大堆函数,它们都需要相同的对象,并且希望它在引擎盖下发生,而不显式地到处传递它。这可以通过将其隐藏在结果类型中来实现

这可用于标准单子组合器:

 quun = (`runReader`c) $ do
   foo <- getFoo     -- `c` argument implicitly passed
   bar <- getBar
   baz <- getBaz
   return (calcQuun foo bar (2*baz))
quon=(`runReader`c)$do

不幸的是,读者monad带来了monad的标准问题->他们不作曲。因此,我要么将所有代码保存在
Reader-Client
的上下文中,要么使用
ReaderT
之类的东西。无论哪种情况,与OOP相比,我都受到了限制。不过谢谢你的详细回答,你证实了我对这个主题的理解:)@Krever
ReaderT
是单子的作曲方式。与OO相比,这并没有限制(我想说:OO语言更为有限,因为它们只有一个monad,
StateT This IO
,而Haskell和Elm允许您为每种情况选择合适的monad)。约束释放了约束和自由约束。仅限于一个monad在编写代码时提供了很大的自由,例如,不必关心正确的转换器堆栈。尽管如此,我想我现在明白了这一点,将记录传递给函数与在对象上调用函数并没有什么不同——运动部件的数量是相同的。发现了这个问题——如果我正确理解这个问题,它是关于如何在FP中进行依赖注入的。如果是这样的话,您可以找到我关于相关主题的系列文章:。总之,在Haskell中,人们要么使用自由单子,要么使用类型类。也许这个F#问题及其答案也能起到启发作用:
 quun = (`runReader`c) $ do
   foo <- getFoo     -- `c` argument implicitly passed
   bar <- getBar
   baz <- getBaz
   return (calcQuun foo bar (2*baz))