“功能性” Swift#2:使用运算符进行合成

直接从上一篇文章开始,我们现在将开始更深入地研究功能性内容。 这是我们最后一次到达的地方:

 让stringToURL:(String)-> URL?  = {URL(string:$ 0)} 
让urlToData:(URL)->数据? = {试试? Data(contentsOf:$ 0)}
让dataToImage:(数据)-> UIImage? = {UIImage(data:$ 0)}
 让stringToImage:(String?)-> UIImage?  = { 
$ 0.flatMap(stringToURL).flatMap(urlToData).flatMap(dataToImage)
}
  _ = stringToImage(“ https://placebear.com/200/300”) 

在可重用性方面,这是对原始版本的巨大改进,但我们可以走得更远。 在每个调用中都会重复flatMap调用,因此我们可以将它们为一组吗?

 让stringToURL:(String)-> URL?  = {URL(string:$ 0)} 
让urlToData:(URL)->数据? = {试试? Data(contentsOf:$ 0)}
让dataToImage:(数据)-> NSImage? = {NSImage(data:$ 0)}
  func fMap (_ fnc:@转义(T)-> U?)->(T?)-> U? 
{
返回{$ 0.flatMap(fnc)}
}
 让flatStringToURL = fMap(stringToURL) 
让flatUrlToData = fMap(urlToData)
让flatDataToImage = fMap(dataToImage)
 让stringToImage:(String?)-> NSImage?  = { 
返回flatDataToImage(flatUrlToData(flatStringToURL($ 0)))
}
  _ = stringToImage(“ https://placebear.com/200/300”) 

嗯……好吧,通用fMap函数确实将flatMap封装到了一个地方,我们最终获得了内置处理函数的版本,但是我们已经失去了以很好的方式链接函数的功能……

我们需要某种表达方式,取该值并将其应用到该函数,然后取该函数并与另一个函数组合。

  func apply (_ fnc:(T)-> U,到值:T)-> U 
{
返回fnc(value)
}
  func组成(f:@转义(A)-> B, 
g:@转义(B)-> C)->(((A)-> C)
{
返回{a in
令b = apply(f,至:a)
返回apply(g,to:b)
}
}

有了这两个功能,我们现在可以做到:

 让flatUrlToImage:(URL?)-> UIImage?  = compose(f:flatUrlToData,g:flatDataToImage) 
 让flatStringToImage:(String?)-> UIImage?  = compose(f:flatStringToURL,g:flatUrlToImage) 
  _ = apply(flatStringToImage,to:“ https://placebear.com/200/300”) 

我们使用compose来创建新功能,这些功能是现有功能的组合,然后将组合功能的最终版本应用于原始字符串。 到达那里……但是现在需要一点操作员魔术来清理它们。

 优先组ForwardApplication 
{
关联性:左
高于:AssignmentPrecedence
}
 优先组ForwardComposition 
{
关联性:左
高于:ForwardApplication
}
 中缀运算符|>:ForwardApplication 
中缀运算符>>>:ForwardComposition
  func |> (a:A,f:(A)-> B)-> B 
{
返回f(a)
}
  func >>> (f:@转义(A)-> B, 
g:@转义(B)-> C)->(((A)-> C)
{
返回{a in
a |> f |> g
}
}

如您所见,这些infix运算符函数的形式与以前的apply和compose函数相同。 现在我们的组成和应用程序看起来像这样:

 让flatStringToImage = flatStringToURL >>> flatUrlToData >>> flatDataToImage 
  _ =“ https://placebear.com/200/300” |> flatStringToImage 

或者,我们可以删除临时的`flat`常量,最后得到:

 优先组ForwardApplication 
{
关联性:左
高于:AssignmentPrecedence
}
 优先组ForwardComposition 
{
关联性:左
高于:ForwardApplication
}
 中缀运算符|>:ForwardApplication 
中缀运算符>>>:ForwardComposition
  func |> (a:A,f:(A)-> B)-> B 
{
返回f(a)
}
  func >>> (f:@转义(A)-> B, 
g:@转义(B)-> C)->(((A)-> C)
{
返回{a in
a |> f |> g
}
}
  func fMap (_ fnc:@转义(T)-> U?)->(T?)-> U? 
{
返回{$ 0.flatMap(fnc)}
}
  // ------------------------------------------------ ------------- // 
 让stringToURL:(String)-> URL?  = {URL(string:$ 0)} 
让urlToData:(URL)->数据? = {试试? Data(contentsOf:$ 0)}
让dataToImage:(数据)-> UIImage? = {UIImage(data:$ 0)}
  _ =“ https://placebear.com/200/300” |> fMap(stringToURL) 
>>> fMap(urlToData)
>>> fMap(dataToImage)

当然,我们可以添加一个新的运算符以包括flatMap部分,但是对我来说,我认为这不太清楚。

定义了运算符和fMap函数后,我们可以添加新的展开函数以在任意数量的类型之间进行转换,并且只要其中一个的输入与下一个的输出匹配,就可以通过任何方式组合它们。

让我们将其与最干净的点语法版本进行比较。

 静态让stringToURL:(String)-> URL?  = {URL(string:$ 0)} 
静态让urlToData:(URL)->数据? = {试试? Data(contentsOf:$ 0)}
静态让dataToImage:(数据)-> UIImage吗? = {UIImage(data:$ 0)}
  _ =“ https://placebear.com/200/300".flatMap(stringToURL) 
.flatMap(urlToData)
.flatMap(dataToImage)

如果我们忽略运算符和fMap定义,则这两种方法似乎都没有什么好处。 运营商的知名度不高,但是我认为使用上下文非常清楚。 实际最大的不同是我们如何创建和使用组合功能:

 让stringToImage:(String?)-> UIImage?  = { 
$ 0.flatMap(stringToURL)
.flatMap(urlToData)
.flatMap(dataToImage)
}
  _ = stringToImage(“ https://placebear.com/200/300”) 

vs:

 让stringToImage = fMap(stringToURL) 
>>> fMap(urlToData)
>>> fMap(dataToImage)
  _ =“ https://placebear.com/200/300” |> stringToImage 

对我来说,后者更清晰,但幅度不大……

无论如何,仅此而已。 稍后将进行另一次探索。