this post was submitted on 14 Aug 2023
1428 points (98.0% liked)

Programmer Humor

19709 readers
137 users here now

Welcome to Programmer Humor!

This is a place where you can post jokes, memes, humor, etc. related to programming!

For sharing awful code theres also Programming Horror.

Rules

founded 1 year ago
MODERATORS
 
you are viewing a single comment's thread
view the rest of the comments
[–] [email protected] 3 points 1 year ago* (last edited 1 year ago)

Here's an example (first in Haskell then in Go), lets say you have some types/functions:

  • type Possible a = Either String a
  • data User = User { name :: String, age :: Int }
  • validateName :: String -> Possible String
  • validateAge :: Int -> Possible Int

then you can make

mkValidUser :: String -> Int -> Possible User
mkValidUser name age = do
  validatedName ← validateName name
  validatedAge  ← validateAge age
  pure $ User validatedName validatedAge

for some reason <- in lemmy shows up as &lt;- inside code blocks, so I used the left arrow unicode in the above instead

in Go you'd have these

  • (no Possible type alias, Go can't do generic type aliases yet, there's an open issue for it)
  • type User struct { Name string; Age int }
  • func validateName(name string) (string, error)
  • func validateAge(age int) (int, error)

and with them you'd make:

func mkValidUser(name string, age int) (*User, error) {
  validatedName, err = validateName(name)
  if err != nil {
    return nil, err
  }

  validatedAge, err = validateAge(age)
  if err != nil {
    return nil, err
  }

  return User(Name: validatedName, Age: validatedAge), nil
}

In the Haskell, the fact that Either is a monad is saving you from a lot of boilerplate. You don't have to explicitly handle the Left/error case, if any of the Eithers end up being a Left value then it'll correctly "short-circuit" and the function will evaluate to that Left value.

Without using the fact that it's a functor/monad (e.g you have no access to fmap/>>=/do syntax), you'd end up with code that has a similar amount of boilerplate to the Go code (notice we have to handle each Left case now):

mkValidUser :: String -> Int -> Possible User
mkValidUser name age =
  case (validatedName name, validateAge age) of
    (Left nameErr, _) => Left nameErr
    (_, Left ageErr)  => Left ageErr
    (Right validatedName, Right validatedAge) => 
      Right $ User validatedName validatedAge