Kre*_*ver 2 haskell functional-programming elm
我想在elm中为外部api创建一个http客户端.在scala中,这是OO/FP混合,我会表达这一点(暂时忘记异步),简单如下:
class Client(url: String) {
def getFoo(): String = ???
}
Run Code Online (Sandbox Code Playgroud)
但在榆树林有点迷失.显而易见的解决方案是将url直接传递给函数
module Client
getFoo : String -> String
Run Code Online (Sandbox Code Playgroud)
但这使用起来非常痛苦,因为它使每个调用都变得复杂,因此负担随着定义的函数数量和对这些函数的调用次数而增加.
我试图使用带有函数的记录,例如:
type alias Client = { getFoo: String }
createClient : String -> Client
Run Code Online (Sandbox Code Playgroud)
但感觉就像是对OOP的模仿.AFAIU由Ocaml中的Functors和OOP中的对象解决.
什么是在Elm(或Haskell,如果Elm缺少某些特定功能)的规范方法?
请记住,调用†的 OO方法只不过是为函数提供额外this/ self参数的语法糖:
-- OO ? functional/procedural
Client c = ...; ? c = ... :: Client
... ? ...
main() {print(c.getFoo());} ? main = print(getFoo c)
Run Code Online (Sandbox Code Playgroud)
因此,使用C语言和FP语言等过程语言,这种路径是非常有可能的,而且通常很有用.
data Client {
url :: String
, ...
}
getFoo :: Client -> String
getFoo (Client{url = u}) = ...
Run Code Online (Sandbox Code Playgroud)
是的,这需要你明确地传递Client对象,但这不一定是坏事 - 如果你有正确的区分类型,可以很明显需要传递什么函数的哪个参数,这个方法实际上比OO方法更好地扩展,因为你可以有多个对象作为参数,每个函数可以只接受它需要的那些.
当然,也有情况下,你也有一大堆的所有需要同一个对象的功能,想拥有它发生在引擎盖下没有明确地传递给它无处不在.这可以通过将其隐藏在结果类型中来完成.
type Reader c r = c -> r
getFoo :: Reader Client String
getBar :: Reader Client Int
getBaz :: Reader Client Double
Run Code Online (Sandbox Code Playgroud)
quun = (`runReader`c) $ do
foo <- getFoo -- `c` argument implicitly passed
bar <- getBar
baz <- getBaz
return (calcQuun foo bar (2*baz))
Run Code Online (Sandbox Code Playgroud)
如果您的方法中也有变异,这种方法特别有用,这在OO中很常见.通过显式传递,这变得非常麻烦,因为您需要使用更新的副本,并且需要小心将正确的版本传递给每个函数.使用状态monad,这将自动处理,就像它是真正的突变一样.
† 我在这里无视遗产.如果通过超类指针调用方法,则会有一个额外的vtable查找,但可以将其建模为记录类型中的另一个字段,告诉您此对象属于哪个子类.