xt · Codeberg.org

The From Project

Welcome to the From Project!

This is a multi-package repo. You may want to see README.md of each package as well.

Synopsis

  • Use from :: a -> b to convert an Int16 to a Word32, or a Data.Text.Lazy.Builder.Builder to a ByteString.
    • The rule of thumb is, if there is a "sane default way" to convert a type to another type, there should be a From instance for that mapping.
    • This way, you don't have to memorize every single conversion function like toStrictByteString or decodeUtf8Lenient.
  • Use tryFrom :: a -> Maybe b for conversions that can fail.
  • "from" (the core package) has 1 dependency: base.
    • "from-string" has 3: base, bytestring, and text.

Usage

The "from" package provides some default instances for the basic integral types.

"from-string" should be useful for nearly all practical Haskell projects. import From.String and use from to convert string types (String, strict ByteString, lazy Bytestring, strict Text, lazy Text, Builder, ...).

Why?

If you are only interested in convenience, you don't need to read on at all. However, if you noticed this is not the first attempt of its kind, you may be wondering how this is a good idea instead of xkcd #927. Let us explain.

Aesthetics and conciseness mattered more than we previously thought

Consider do: If you've been following the Haskell ecosystem for a while, you probably would have noticed how libraries began declaring a Monad instance to types that are not quite Monad. This happens because they want the do notation for something that is, in fact, Semigroup.

They all define a type SomethingM a, and then add a type synonym like type Something = SomethingM (). This way, they can provide primitives that are Something, and these primitives can be composed in a do notation; The library user can simply put one item per line.

someChain = do
  a
  b
  c

Some people suggested adding mdo or be for this. But, after all...

someChain = be
  a
  b
  c

is actually

someChain = mconcat
    [ a
    , b
    , c
    ]

... and the Haskell Committee was reluctant, because they thought , or [] couldn't possibly be such horrible syntactic noise to grant a new syntax.

But in the meantime, we now see so many packages that do this. cereal does this. lucid does this. shakespeare does this. People love to (ab)use the do notation to express semigroups!

I cautiously suggest that aesthetics is one of the reasons why convertible did not really catch on. It provides convert, and it has these subjective issues:

  • The name convert is long.
    • I know, this can't possibly be a legitimate reason, but then, [,] can't possibly be a reason to define a whole set of unnecessary Functor, Applicative, and Monad instances. But people do this!
    • Consider how acc (instead of accumulator) is a widespread name in Haskell. Usually acc is only needed for recursive functions, yet people choose to abbreviate it. convert is needed in far more occasions.
  • convert something does not sound right.
    • It's supposed to be a pure function, but in English, convert something may sound like action, instead of expression. Compare convert something to from something as "expression."
  • We already have the from convention in base: fromIntegral, fromEnum, fromMaybe, fromException, fromString, fromList, ...

Dependencies matter

Type conversion is such a common need, that its typeclass (and functions) should be one of the building blocks for all other projects.

Therefore it really should not have more dependencies than base. In practice, one library with many dependencies will cause a long download time, long build time, and huge build results that is far more bloated than necessary. It also make the whole project more brittle: Build breaks, for whatever reason.

But convertible has 6 dependency packages. That's 13 if you include transitive dependencies: array, binary, bytestring, containers, deepseq, mtl, old-locale, old-time, pretty, template-haskell, text, time, and transformers. lawful-conversions has 11, which is 44 with transitive dependencies.

There are perfectly legitimate use cases of Haskell that require type conversion but do not need any of those dependencies, so I suggest we keep a library that provides the type conversion class without any dependencies (other than base) at all.

Read the original on codeberg.org ↗