我正在尝试坚持纯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并不重要),但我看不出它是如何帮助的(我们需要mock
SeekableByteChannel
1}},但现在已包含在IO
)中。
你能帮助我以纯FP风格设计这个功能(或者至少让它不那么丑陋)?
答案 0 :(得分:4)
When you need to wrap impure code, most of the time (based on my experience), it's not going to "be pretty". But, what we gain is that we only have a single point which deals with the "messy stuff", and we get a nice abstraction from there on.
What we want is to create a stream which is bound by an IO
effect. Instead of Stream[Id, SeekableByteChannel]
, we're really in Stream[IO, SeekableByteChannel]
because we are in the IO
effect context:
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)
}
}
}
}
This way, we suspend the side effect, which is what we want to make these side effectful computation RT, and apply transformations on the stream from this point on.