Skip to content

migrate to fs2-io - #257

Closed
i10416 wants to merge 5 commits into
typelevel:masterfrom
i10416:improve-io
Closed

migrate to fs2-io#257
i10416 wants to merge 5 commits into
typelevel:masterfrom
i10416:improve-io

Conversation

@i10416

@i10416 i10416 commented Feb 20, 2022

Copy link
Copy Markdown
Contributor

Hello.

In this PR, I replaced java.io stuffs with fs2.io.Files and fs2.io.Path.

I tried to hide changes and keep user-facing signature as possible except for Sync/Async constraint, but I think some API changes are unavoidable.

It makes sense to use fs2 library for io module despite additional dependencies because fs2 helps cross platform io operations as mentioned in #231. It will be great if laika-io runs on js too.

@i10416

i10416 commented Feb 20, 2022

Copy link
Copy Markdown
Contributor Author

Btw, is there any reason not to use formatter plugin?

@i10416

i10416 commented Feb 20, 2022

Copy link
Copy Markdown
Contributor Author

dep #258

@armanbilge

Copy link
Copy Markdown
Member

Wow, nice, thanks for working on this! I'm happy to help review this one :)

@@ -78,6 +82,12 @@ object TextInput {
TextInput[F](path, docType, InputRuntime.textFileResource[F](file, codec).map(StreamReader(_, file.length.toInt)), Some(file))
def fromStream[F[_]: Sync] (path: Path, docType: TextDocumentType, stream: F[InputStream], codec: Codec, autoClose: Boolean): TextInput[F] =

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, I think all uses of F[InputStream] should actually be replaced with fs2.Stream[F, Byte]. It is a referentially transparent, pure-FP, and fs2.io includes methods to get fs2.Stream from classpath resources etc. while correctly handling blocking.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I feel the same. However, I think we should be careful because i/o stream is such a fundamental component for io module that it will propagate changes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately I have not yet looked at this PR in detail, but as a general tendency, I feel like while it would make sense to standardise on fs2.Stream for any internal API wherever feasible, I think I'd still like to offer InputStream in the public APIs, since it is such a common type and this lib is also intended for an audience that does not know that much about Typelevel libs. The implementation of such a method could then immediately translate down to fs2 wherever possible.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd still like to offer InputStream in the public APIs, since it is such a common type and this lib is also intended for an audience that does not know that much about Typelevel libs.

💯 I definitely agree with this sentiment, with regard to accessibility.

However, in this case I would argue that signatures F[InputStream] are actually more likely to be abused by the uninformed user than fs2.Stream[F, Byte].

A user who doesn't know much about Cats Effect could very plausibly write F.pure where they meant F.delay or even worse F.blocking and cause all sorts of problems for Laika/Cats Effect which make many important assumptions about the semantics of code suspended in F[_] (for example, at which point side-effects vs pure are evaluated and on which threadpool).

On the other hand, It seems reasonable to add scaladocs to point a user to methods such as fs2.io.readClassResource("myResource") to get an fs2.Stream[F, Byte], which I think are easy to use, hard to abuse, and do the right thing out-of-the-box.

To offer an accessible "unsafe" API, FWIW I think you should do away with F[_] entirely which I suspect is confusing for this group of users as well. This API can be then implemented in an imperative style and take the appropriate measures not to violate the assumptions of the Cats Effect runtime.

@jenshalm jenshalm Feb 26, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was actually thinking about the specific use case of interop with existing tools that provide an InputStream, not the simpler scenarios of classpath or file resources. Removing the existing fromStream method does not make anything safer here, since it just moves the problem to fs2.io.readInputStream which also expects an F[InputStream]. The way I see it it's just more code to write for the user and the same kind of trap to fall into.

My preference would be not to remove any existing entry-point API, but instead only add fromStream(fs2.io.Stream) and then use fs2 for most of the internal API. Which means to also keep fromFile(String) and fromClasspathResource(String), etc.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it's a good point to improve scaladoc for users not familiar with cats-effect. From working with junior devs in my day job I am aware that confusing F.delay, F.pure and F.blocking is amongst the no1 sources of bugs when using effects (to some degree even for seniors, to my surprise).

@i10416

i10416 commented Feb 26, 2022

Copy link
Copy Markdown
Contributor Author

Thank you @armanbilge for taking time to review this PR. I will tweak this pr in a few days.

@jenshalm jenshalm added this to the 0.19.0 milestone Feb 26, 2022
@i10416
i10416 force-pushed the improve-io branch 3 times, most recently from e1420eb to 946f9d0 Compare April 14, 2022 04:50
@jenshalm

Copy link
Copy Markdown
Contributor

Apologies for the very late and incomplete feedback you received on this PR! The main problem was that I was unable to spot some of the issues here just by browsing the PR without actually working on this locally myself, which I did not find the time for until last week.

I'll be closing this as superseded by #281 now which is an umbrella ticket that lists all the PRs for the fs2 migration. There are several issues that would have needed to be addressed in this PR and discussing them all in a PR that attempts to do the whole migration in one step appeared too difficult and time-consuming.

Some, but not all, of the issues were:

  • BinaryOutput cannot be migrated to fs2 at all, for reasons explained in detail in Migration steps for using fs2 in laika-io module #281 (quick summary: the EPUB and PDF renderer are not compatible with fs2.Pipe)
  • The duplication of model types (e.g. BinaryInput and BinaryInput2) would be difficult to maintain, ideally all those types need to converge early before being routed through the inner runtime layers.
  • There are some specific integration challenges that needed to be addressed for EPUB and PDF output as part of the migration.
  • User-facing high-level API should ideally not break unnecessarily, while public-internal APIs can be reworked in a release that breaks binary-compatibility. The distinction between user-facing and public-internal APIs is not very clear in Laika (and difficult to document thoroughly), so this is something you could not easily be aware of.
  • Recent commits of this PR did not compile.

Thanks a lot for kicking this off and investing some time into this! I promise I try to give feedback earlier in the future or at least recommend to suspend the work until I performed some basic sanity checks on the chosen approach.

@jenshalm

Copy link
Copy Markdown
Contributor

Closing as superseded by #281

@jenshalm jenshalm closed this Apr 29, 2022
@jenshalm jenshalm removed this from the 0.19.0 milestone Apr 29, 2022
@i10416

i10416 commented Apr 29, 2022

Copy link
Copy Markdown
Contributor Author

Thank you for feedback to this PR and proposing better gradual migration PRs!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants