我试图坚持纯FP风格,并希望设计一个引用透明的函数。
我有一个java.nio.channels.SeekableByteChannel
,这是一个数据源。一旦我打开文件并获取SeekableFileChannel
实例,我就需要读取文件的第一行并使用这些行来确定查找位置。
所以我创建了以下函数:
object AdjustChannelAndGetStream {
def apply(ch: SeekableFileChannel)
(firstChunksToOffset: List[Array[Byte]] => Long): fs2.Stream[Id, Array[Byte]] {
val offset = //depending on the first bytes read from the file
//get the number of bytes read before
val newChannel = ch.position(offset)
//finally wrap newChannel into fs2.Stream
}
}
问题是,该功能看起来很丑。它不会暂停副作用,这使得测试变得困难(模拟SeekableByteChannel
(。
我倾向于将SeekableByteChannel
包装成IO[SeekableByteChannel]
(Scalaz/Cats 无关紧要(,但我看不出它有什么帮助(我们需要相同的SeekableByteChannel
mock
,但现在包装成IO
(。
你能帮我以纯FP风格设计这个功能(或者至少让它不那么丑陋(吗?
当你需要包装不纯的代码时,大多数时候(根据我的经验(,它不会"漂亮"。但是,我们得到的是,我们只有一个点来处理"混乱的东西",从那里我们得到了一个很好的抽象。
我们想要的是创建一个受IO
效果约束的流。而不是Stream[Id, SeekableByteChannel]
,我们真的处于Stream[IO, SeekableByteChannel]
,因为我们处于IO
效应上下文中:
import java.nio.channels.SeekableByteChannel
import cats.effect.IO
object AdjustChannelAndGetStream {
def apply(ch: SeekableByteChannel)(
firstChunksToOffset: List[Array[Byte]] => Long)
: fs2.Stream[IO, SeekableByteChannel] = {
fs2.Stream.eval {
IO {
val offset: Int = ???
ch.position(offset)
}
}
}
}
这样,我们暂停副作用,这就是我们想要使这些副作用计算 RT 的原因,并从这一点开始对流应用转换。