Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Speaking of types, what are your thoughts on fp-ts if you've used it? It brings functional programming concepts like in Haskell such as monads into TypeScript.


haven’t used it myself but other teams at the company I work for have tried with mixed results.

It’s very opinionated about the way you structure your code and basically makes anything thats not fully fp-ts hard to integrate, and also is quite hard for general JS people to wrap their head around.

It’s been designed by FP people for FP people and if there are some on your team who are not fully on board or are just starting to learn FP - expect lots of friction.

At my company it was mostly scala coders and “cats” lovers (category theory stuff lib for scala) mixed in with regular nodejs devs and I could sense a lot of animosity around fp-ts and its use.

But on a more practical note, the more they converted their codebase to fp-ts the more they reported massive compile time slowness. Like it would start to take minutes to compile their relatively isolated and straight forward services.

From what I gathered, if you want to go fp-ts its just too much friction and you’re much better off picking up a language designed from the bottom up for that - scala / ocaml / elixr / etc.

To be honest once I’ve been comfortable enough with the more advanced TS features, you can write plain old javascript in a very functional style, and thats actually pretty great, especially if you throw date-fns, lodash/fp or ramda into the mix, and it remains largely approachable to people outside of FP and you can easily integrate external libs.


Totally agree. I wrote my thoughts and experiences on FP-TS and maybe those csn help -https://rockyj.in/2022/03/24/fun-with-composition-2

IMHO, functional TS is great with ramda, currying etc. and solves a lot of problems nicely. See also https://mostly-adequate.gitbook.io/mostly-adequate-guide/ch0...


That sounds about what I've expected. Frankly in a TS codebase with many other devs that are not versed in FP, I wouldn't want to bring in a pure FP library because it, like you said, needs everyone to understand the "meta-language" of FP so to speak, such as how monads work, not having raw side effects, mutation etc.

Ramda et al seem like a good compromise. Looking through its docs though, doesn't JS have a lot of this stuff covered? ie filter, map, reduce etc. What new stuff is it bringing in that covers say the 90% of most use cases?


Well currying is a big one, and also functions like flow/pipe (in lodash/fp) mimic piping in FP languages which allows for very nice expressions of business logic for modifying data.

You can sometimes loose the type though, so I prefer to do it with off the shelf filter/map/reduce even if its a bit unsightly.

I personally reach for lodash/fp for more specific expressions like orderBy or groupBy. There are some very nice well documented and powerful primitives there.

At one team a guy got so enamored with the functional style that he went ahead and rewrote mountains of logic in lodash/fp. And it turned out quite hard to maintain by the rest of the team, so you also have the danger of “overdoing it for the rest of the team” danger as well.


I haven't used fp-ts directly, but I use an adjacent package that declares fp-ts as a peer dependency: io-ts. I've almost exclusively for easier type management during deserialization. In vanilla TypeScript I would have defined an interface and a user-defined type guard to handle deserialization:

interface ClientMessage { content: string }

function isClientMessage(thing: unknown): thing is ClientMessage { return thing !== null && typeof thing === 'object' && typeof thing.content === 'string' }

expect(isClientMessage('nope')).toBeFalse()

expect(isClientMessage({ content: 'yup' })).toBeTrue()

but user-defined type guards basically duplicate the interface, are prone to error, and can be very verbose. io-ts solves this by creating a run-time schema from which build-time types can be inferred, giving you both an interface and an automatically generated type guard:

import { string, type } from 'io-ts'

const ClientMessage = type({ content: string })

expect(ClientMessage.is('nope')).toBeFalse()

expect(ClientMessage.is({ content: 'yup' })).toBeTrue()

Very nifty for my client/server monorepo using Yarn workspaces where the client and server message types are basically just a union of interfaces (of various complexity) defined in io-ts. Then I can just:

ws.on('message', msg => {

  if (ClientMessage.is(msg)) {

    // fullfill client's request

  } else {

    // handle invalid request

  }
})

Only thing missing is additional validation, which I think can be achieved with more complicated codec definitions in io-ts.


fp-ts [0] and the accompanying io-ts [1] are very well designed and neatly implemented. I'd consider them the reference for all things FP in Typescript.

In practice though I find that they don't mesh well with the language and ecosystem at large. Using them in a React/Vue/Whatever app will catch you at every step, as neither the language nor the frameworks have these principles at their core. It takes a lot of effort to escape from their gravitational pull. Using Zod [2] for your parsing needs and strict TS settings for the rest feel more natural.

It could work in a framework-agnostic backend or logic-heavy codebase where the majority of the devs are in to FP and really want / have to use Typescript.

0 - https://gcanti.github.io/fp-ts/

1 - https://gcanti.github.io/io-ts/

2 - https://zod.dev


If you stick to a few useful types like `Option` and `Either`/`TaskEither` you can get a lot of value out of it when writing server side code, particularly when combined with `io-ts` for safely parsing data.

If you go all in and use every utility it provides to write super succint FP code, it can get pretty unreadable.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: