将不纯函数转换为纯函数-Scala

将不纯函数转换为纯函数-Scala,scala,functional-programming,pure-function,Scala,Functional Programming,Pure Function,我目前的设计是我将有一个服务类,负责通过外部API获取一些信息。现在我明白了不可能有100%的纯函数 我的问题:处理需要在Scala/FP中连接到外部API的情况的最佳方法是什么?。目标是通过最小化不纯函数,获得最合适的“函数编程风格” 目前,我正在IO对象中封装所有API调用。这是否足够合适?我看到一些单子的例子。在这种情况下,我应该合并monad样式吗 这不是一个FP问题,因为我看不出您的代码在FP方面有任何问题,但在我看来,您应该做的是使用依赖项注入,这样,对于测试,您可以用一个测试类替换

我目前的设计是我将有一个服务类,负责通过外部API获取一些信息。现在我明白了不可能有100%的纯函数

我的问题:处理需要在Scala/FP中连接到外部API的情况的最佳方法是什么?。目标是通过最小化不纯函数,获得最合适的“函数编程风格”


目前,我正在IO对象中封装所有API调用。这是否足够合适?我看到一些单子的例子。在这种情况下,我应该合并monad样式吗

这不是一个FP问题,因为我看不出您的代码在FP方面有任何问题,但在我看来,您应该做的是使用依赖项注入,这样,对于测试,您可以用一个测试类替换具有保证响应的
IO
。大概是这样的:

object IO {

  def getHtmlFromWebsiteViaHttp(link: String, apiKey: String = ""): String = {
    Http(link)
      .param("access_token", apiKey)
      .asString
      .body
  }
}

class SongService {
  private def retrieveSongId(songName: String): Option[JsValue] = {
    val formattedSongName = songName.replace(" ", "%20")
    val searchLink = "https://api.genius.com/search?q=" + formattedSongName

    //impure call
    val geniusStringResponse = IO.getHtmlFromWebsiteViaHttp(searchLink, apiKey)

   //Extra processing on geniusStringResponse
  }
}
然后为您服务:

abstract class IO {
  def getHtmlFromWebsiteViaHttp(link: String, apiKey: String = ""): String
}

class IOImpl extends IO {
  def getHtmlFromWebsiteViaHttp(link: String, apiKey: String = ""): String = {
    Http(link)
      .param("access_token", apiKey)
      .asString
      .body
  }
}

class IOTestImpl extends IO {
  def getHtmlFromWebsiteViaHttp(link: String, apiKey: String = ""): String = ??? //some test HTML
}

然后,当您实例化您的
SongService
时,将其在测试中传递
IOTestImpl
,否则传递
IOImpl
。您可能会在和上找到一些相关信息。

“我正在将所有API调用封装在IO对象中”-您的意思是,您只是将不纯函数移到一个名为
“IO”
对象中?我认为这有点过于简单化了…对。因此,如果这不是实现这一目标的最佳方式,那该怎么做呢?我不确定这是多么不纯洁。您只是从外部资源获取一个值。对我来说,似乎是透明的。正如@Andreytukin所说,将代码移动到一个单独的对象只是。。。移动代码。它不会(实际上)影响你的程序的运行。是的,但是从外部资源获取价值的行为不是天生的不纯吗?无法保证输出与指定输入相同。(404错误或301正常)@Lasf它不是引用透明的,因为它很容易由于外部状态的变化而给出不同的结果,例如您试图访问的服务器停机或计算机失去internet访问。更不用说API的歌曲列表可能会定期更新。
class SongService(io: IO) {
  private def retrieveSongId(songName: String): Option[JsValue] = {
    val formattedSongName = songName.replace(" ", "%20")
    val searchLink = "https://api.genius.com/search?q=" + formattedSongName
    val geniusStringResponse = io.getHtmlFromWebsiteViaHttp(searchLink, apiKey)
   //Extra processing on geniusStringResponse
  }
}