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

feat(Maybe): introduce Maybe #3611

Closed
wants to merge 3 commits into from
Closed

feat(Maybe): introduce Maybe #3611

wants to merge 3 commits into from

Conversation

pierrebeitz
Copy link
Contributor

@pierrebeitz pierrebeitz commented Feb 20, 2019

Introduces a Maybe-Type. Have a look at the docs and examples to get a feeling for it.

Besides reading nicer than e.g. string | undefined when modelling stuff with types and being a building block for other constructs that aim to push type-safety and -inference as far as possible, the most tangible things for now are

  1. a way to circumvents checks for undefined

    import Maybe from 'utils/maybe';
    import pipe from 'utils/pipe'; // pipe is not typed yet - so this example is fictional
    
    // one common way to map a value that's not guaranteed to be present
    type toUpper1 = (in: string | undefined) => string | undefined;
    const toUpper1 = s =>
      (s !== undefined) 
        ? s.toUpperCase()
        : undefined;
      }
    
    // the way this proposal enables
    type toUpper2 = (in: Maybe<string>) => Maybe<string>;
    const toUpper2: toUpper2 = Maybe.map(x => x.toUpperCase())
  2. It's a nudge to prevent ?-properties:

    // before
    interface Person {
      name: string;
      age?: number;
    }
    
    // after
    interface Person {
      name: string;
      age: Maybe<number>;
    }

Maybe<number> is rather equivalent to age: number | null, which currently is rarely used. I suspect because the ? reads better and it's often times less work to just directly pass around undefineds.


We have 2 options regarding DX:

  1. Curried, data-last style

    import { pipe } from "rxjs";
    const getAgeInWords: (age: Maybe<number>) => string = pipe(
        Maybe.map((age: number) => age + 1),
        Maybe.map((age: number) => `is ${age} years old.`),
        Maybe.withDefault("wants to stay timeless.")
    )
    • reads nice with RxJS' pipe.
    • would match the style of lodash/fp and ramda.js
  2. Data-first style

    import { pipe } from "rxjs";
    const getAgeInWords: (age: Maybe<number>) => string = pipe(
        _ => Maybe.map(_, age => age + 1),
        _ => Maybe.map(_, age => `is not yet ${age} years old.`),
        _ => Maybe.withDefault(_, "wants to stay timeless.")
    )
    • infers all the types!

I chose to start an own implementation of functional helpers, as

  • library code can be hard to grok in the beginning. (here's their maybe)
  • ... fantasy-land-compliant-stuff can be even harder to grok - also the APIs are a little too cluttered for my taste
  • crafting our own implementation along with sound types seems more reasonable than relying on a patchwork of different libraries of varying type-quality

I will rely a lot on stuff i know from elm as i experienced it as being extremely well thought through.

@pierrebeitz pierrebeitz force-pushed the pb/introduce-maybe branch 3 times, most recently from c220ffe to 8d45090 Compare February 20, 2019 08:38
@pierrebeitz
Copy link
Contributor Author

pierrebeitz commented Feb 20, 2019

@GeorgiSTodorov as there are no dependencies to existing code, there's no need to merge master and hassle the CI-Server until discussions are settled. Thanks nonetheless! 😃

@DanielMSchmidt
Copy link
Contributor

DanielMSchmidt commented Mar 15, 2019

I would vote for Number 1 as it has more developer ergonomity, though we don't get the types inferred :)

DanielMSchmidt
DanielMSchmidt previously approved these changes Mar 15, 2019
@pierrebeitz
Copy link
Contributor Author

It looks like there's some traction on the inference-issue, so let's go with 1.

Copy link
Contributor

@nLight nLight left a comment

Choose a reason for hiding this comment

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

I would like to avoid having Maybe without everyone fully understand the concept.
Starting from a Monad.

@pierrebeitz
Copy link
Contributor Author

pierrebeitz commented Apr 11, 2019

@nLight I don't think that's necessary. Here's why: https://www.youtube.com/watch?v=oYk8CKH7OhE&feature=youtu.be&t=1455 (till ~28:10). TL;DR: the elm docs introduce the concepts of Maybe and Either (disguised as Result) really effectively without ever mentioning a single fancy FP-term. Their remarkably good API-design allows to learn as you go - just like we did with Typescript and RxJS (Observable is pretty monad'y as well, right?).

In the past i have felt that introducing stuff like that encourages more sophisticated state and type modelling which in turn makes the work with typescript much easier.

While i see that i'm only a very small part of the team and thus the risk of it being introduced and then abandoned seems rather high, i will be owning and spreading this - and i'd throw it out again if we feel that it is not helpful for us.

let's try please. 🙏

@pierrebeitz
Copy link
Contributor Author

might be in as soon as someone spikes decoders into DCOS-UI.

as we settled on the uncurried style to make the most of TS's current inference i'm closing this PR.

@pierrebeitz pierrebeitz deleted the pb/introduce-maybe branch August 30, 2019 06:57
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.

4 participants