Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

On the topic of importing #1633

Open
hesxenon opened this issue Dec 8, 2021 · 5 comments
Open

On the topic of importing #1633

hesxenon opened this issue Dec 8, 2021 · 5 comments

Comments

@hesxenon
Copy link

hesxenon commented Dec 8, 2021

🚀 Feature request

I think the current way of importing/using parts of fp-ts is a bit awkward and it would be nice to be able to import what you need directly from fp-ts in a capitalized way.

In the tutorials I can find around fp-ts (and in general, I think react made this big?) modules are often imported like

import * as Module from "module"

Notice the capitalized namespace.

Current Behavior

When trying to do this with fp-ts we can either write

import * as Option from "fp-ts/Option"

which forces the user to manually import each module. Or we can just write option and let autocomplete do

import { option } from "fp-ts"

But it's a bit weird to define something like type x = option.Option<number>.

Desired Behavior

What I (and hopefully others) really want is

import { Option } from "fp-ts"

type x = Option<number>
const getNumber = Option.getOrElse(0)

and so on.

Suggested Solution

Well, just capitalize all the imports and exports in index.ts. I don't get why you chose to use capitalized filenames and then you lowercased the imports in the first place :)

Or, if you do not want a breaking change (even though I've never seen anybody actually import directly from "fp-ts" in my vast 2 month experience with fp-ts) you could of course just duplicate all imports and exports.

Who does this impact? Who is this for?

All users, but in a big part beginners I think, because in my experience people have come to expect that "public" api is exported from the top level. There are - as you probably know - even eslint rules to disallow "sub-package" imports.

Describe alternatives you've considered

  • Aliasing the imports everywhere.
  • writing an "augmentation file" that just imports and re-exports accordingly

Additional context

Since there is already an open issue that suggests default-exporting the main type of a module - with sensible arguments against it - I'd be more than happy if instead of Option being a type and a namespace (given import { Option } from "fp-ts") it's still just a namespace that exports an additional type t. Seems to be convention in ML world?
But for the sake of all that is holy, make t an alias to option, otherwise you'll end up with type info like t<t<string>, t<t<t<number>>>> because of type expansion.

Your environment

Software Version(s)
fp-ts 2.11.5
TypeScript 4.5.2
@khusmann
Copy link

Newbie here. @hesxenon what you describe as "desired behavior" is how I naively expected the imports to work.

Until I found this issue I was pretty confused trying to follow the blog documentation (thanks @hesxenon) it looked like it was recently updated, but since then import { option } from fp-ts/Option was deprecated so it threw me for a loop.

What about:

import { option as Option } from "fp-ts"

? It still means we have to do the undesirable Option.Option, but aesthetically I prefer that to option.Option. (but am newb so my style sense doesn't count for much)

But yeah, speaking as a newbie, @hesxenon's desired behavior would have "clicked" for me right out of the box:

import { Option } from "fp-ts"

type x = Option<number>
const getNumber = Option.getOrElse(0)

Is there some advantage to Option.Option/option.Option?

@enricopolanski
Copy link

import { Option } from "fp-ts"

I find this confusing, am I importing the Option type or the Option namespace?

import { option as Option } from "fp-ts"

This is clearly a namespace.

In the end I think that current module pattern is the best compromise we can have and it's in the spirit of exporting multiple modules.

In fact, if anything, I would discourage to import directly from fp-ts, without specifying the module, I'm not sure there's many benefits.

@hesxenon
Copy link
Author

hesxenon commented Dec 19, 2021

am I importing the Option type or the Option namespace

That's why I suggested the .t approach. Otherwise your example probably should be made even more explicit with import type { Option } from "fp-ts/Option". Also you never know from a plain import { X } from "x" whether it's a type or a (canonically capitalized) class. This is exactly why I like the ML approach of lowercasing types and uppercasing modules. You always know what you're dealing with, if it's lowercased it's a type or value (or even both in which case the usage clearly determines it) and if it's uppercased it's a module.

the current module pattern

I guess this is where the confusion comes from... what is the current module pattern? import * as Option from "fp-ts/Option"? import { option as Option } from "fp-ts"?

it's in the spirit of exporting multiple modules

With the above in mind, what's not in the spirit of exporting multiple modules with the suggested approach?

I think it's often good to be explicit about imports, but to make the user import every module from the given subspace strongly reminds me of the whole "avoid * imports" discussion. Kevlin Henney has a really interesting talk about this (think it's from NDC conferences) that I'd like to link here if youtube wasn't down right now :/

Anyway I've gone ahead and created such an "aliasing" package with the original copyright license under @hesxenon/fp-ts (@khusmann )

@mikearnaldi
Copy link
Contributor

There are other issues discussing the same thing, the desired behaviour isn't possible, in your snippet Option is both a type and a namespace re-export and unfortunately ts gets confused in that case

@hesxenon
Copy link
Author

yeah, but the Option.t approach is very much possible. As stated this would be my preferred solution anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants