3 ms·
Respectfully, I don't think this articles uses monads correctly, because it's not using any. This could be very elegant: getUser: Option[User] getFriends(u
by otter-in-a-suit 3y ago
Respectfully, I don't think this articles uses monads correctly, because it's not using any. This could be very elegant:
getUser: Option[User]
getFriends(u: User): Seq[Friend]
bestFriends(f: Seq[Friend]): Seq[Friend]
renderFriends(f: Seq[Friend]): Option[UI] // Unit or type UI or HTML or ...
Only `getUser` actually returns an option and is explicit about it. `renderFriends` could arguably do without.
To call, we can do
bestFriends: Option[Seq[Friends]] = getUser.flatMap(u: User => renderFriends(bestFriends(getFriends(u))))
The render function could either gracefully render an empty list or error check as part of the `flatMap`, which takes the form of
flatMap[B](f: A => Option[B]): Option[B]
I really, really dislike it when functions signatures are lying to me, since `User` is clearly != `Option[User]` and `null` will not fit the type semantics of `User`, whatever those are.
And if you don't _call_ it mondads (but rather something more approachable), it's not that wild and scary sounding a concept all of a sudden.
That way, your compiler error checks null-type scenarios for you, your type signatures are clean, don't lie, and your compiler forces you to explicitly do something like `runSafely` (or `runUnsafe` etc.), usually a single point of failure.
Bonus, `MonadError`-type constructs are awesome too, since I get
handleErrorWith[A](fa: F[A])(f: E => F[A]): F[A]
type functions (this is from cats in scala) to deal with errors explicitly.
- shiandow 3y agoIf anything the real lesson here is that you shouldn't try to lift your functions manually in the presence of a Monad. Monads tend to be somewhat 'infectious' in that anything that touches the Monad will need to be monadic. It's the reason why 'nullable' and 'async' can end up transforming most of the code-base to support their use. And if you are going to write a sum type do it properly. If the language doesn't provide sum types but does have function types just use the category theory definition: function maybeWithUser<T>(withUser: User => T, default: () => T): T { if (!loggedIn) return default() return withUser(fetchUser()); } Wrap this in a class if you really want to, but the idea is the same. This then results in pretty much the code he lists in example 1, exactly because most of the functions are just regular functions: function Page() { const bestFriends = maybeWithUser(user => { const friends = getFriends(user); const bestFriends = filterBestFriends(friends); return render(bestFriends); }, null); return <> {bestFriends} </>; } Of course sometimes it's better to just use what you have rather than try to use language features that aren't quite there. It helps if you can recognise what's going on though.