these: An either-or-both data type & a generalized 'zip with padding' typeclass

This is a package candidate release! Here you can preview how this package release will appear once published to the main package index (which can be accomplished via the 'maintain' link below). Please note that once a package has been published to the main package index it cannot be undone! Please consult the package uploading documentation for more information.

[maintain] [Publish]

This package provides a data type These a b which can hold a value of either type or values of each type. This is usually thought of as an "inclusive or" type (contrasting Either a b as "exclusive or") or as an "outer join" type (contrasting (a, b) as "inner join").

The major use case of this is provided by the Align class, representing a generalized notion of "zipping with padding" that combines structures without truncating to the size of the smaller input.

Also included is ChronicleT, a monad transformer based on the Monad instance for These a, along with the usual monad transformer bells and whistles.

For a dependency light version, check https://hackage.haskell.org/package/data-or package.


[Skip to Readme]

Properties

Versions 0.2, 0.3, 0.4, 0.4.1, 0.4.2, 0.6.0.0, 0.6.1.0, 0.6.2.0, 0.6.2.1, 0.7, 0.7.1, 0.7.2, 0.7.3, 0.7.4, 0.7.5, 0.7.6, 0.7.6, 0.8, 0.8.1, 1, 1.0.1, 1.1, 1.1.1, 1.1.1.1, 1.2, 1.2.1
Change log CHANGELOG.md
Dependencies aeson (>=1.4.2.0 && <1.5), base (>=4.5.1.0 && <4.13), base-compat (>=0.10.5 && <0.11), bifunctors (>=5.5.3 && <5.6), binary (>=0.5.1.0 && <0.10), containers (>=0.4.2.1 && <0.7), data-default-class (>=0.1.2.0 && <0.2), deepseq (>=1.3.0.0 && <1.5), ghc-prim, hashable (>=1.2.7.0 && <1.3), keys (>=3.12.1 && <3.13), lens (>=4.17 && <4.18), mtl (>=2.1.3 && <2.3), QuickCheck (>=2.12.6.1 && <2.13), semigroupoids (>=5.3.1 && <5.4), semigroups (>=0.18.5 && <0.19), transformers (>=0.3.0.0 && <0.6), transformers-compat (>=0.6.2 && <0.7), unordered-containers (>=0.2.8.0 && <0.3), vector (>=0.12.0.2 && <0.13), vector-instances (>=3.4 && <3.5) [details]
License BSD-3-Clause
Author C. McCann
Maintainer oleg.grenrus@iki.fi
Category Data, Control
Home page https://github.com/isomorphism/these
Source repo head: git clone https://github.com/isomorphism/these.git
Uploaded by phadej at 2019-01-26T19:52:31Z

Modules

[Index] [Quick Jump]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


Readme for these-0.7.6

[back to package description]

These — an either-or-both data type

Build Status

The type These a b represents having either a value of type a, a value of type b, or values of both a and b:

data These a b = This a | That b | These a b

This is equivalent to Either (a, b) (Either a b). Or equivalent to Either a (b, Maybe a). Or various other equally equivalent types. In terms of "sum" and "product" types, These a b is a + b + ab which can't be factored cleanly to get a type that mentions a and b only once each.

The fact that there's no single obvious way to express it as a combination of existing types is one primary motivation for this package.

A variety of functions are provided in Data.These akin to those in Data.Either, except somewhat more numerous on account of having more cases to consider. Most should be self-explanatory if you're already familiar with the similarly-named functions in Data.Either and Data.Maybe.

here and there are traversals over elements of the same type, suitable for use with Control.Lens. This has the dramatic benefit that if you're using lens you can ignore the dreadfully bland mapThis and mapThat functions in favor of saying over here and over there.

Align — structural unions

There is a notion of "zippy" Applicatives where liftA2 (,) behaves like zip in the sense that if the Functor is regarded as a container with distinct locations, each element of the result is a pair of the values that occupied the same location in the two inputs. For this to be possible, the result can only contain values at locations where both inputs also contained values. In a sense, this is the intersection of the "shapes" of the two inputs.

In the case of the zip function itself, this means the length of the result is equal to the length of the shorter of the two inputs.

On many occasions it would be more useful to have a "zip with padding", where the length of the result is that of the longer input, with the other input extended by some means. The best way to do this is a recurring question, having been asked at least four times on Stack Overflow.

Probably the most obvious general-purpose solution is use Maybe so that the result is of type [(Maybe a, Maybe b)], but this forces any code using that result to consider the possibility of the list containing the value (Nothing, Nothing), which we don't want.

The type class Align is here because f (These a b) is the natural result type of a generic "zip with padding" operation--i.e. a structural union rather than intersection.

I believe the name "Align" was borrowed from a blog post by Paul Chiusano, though he used Alignable instead.

Unalign

unalign is to align as unzip is to zip. The Unalign class itself does nothing, as unalign can be defined for any Functor; an instance just documents that unalign behaves properly as an inverse to align.

Crosswalk

Crosswalk is to Align as Traversable is to Applicative. That's really all there is to say on the matter.

Bicrosswalk

<cmccann> elliott, you should think of some more instances for Bicrosswalk one of these days
<shachaf> cmccann: Does it have any instances?
<elliott> cmccann: unfortunately it is too perfect an abstraction to be useful.

ChronicleT — a.k.a. These as a monad

These a has an obvious Monad instance, provided here in monad transformer form.

The expected use case is for computations with a notion of fatal vs. non-fatal errors, like a hybrid writer/exception monad. While running successfully a computation carries a "record" of type c, which accumulates using a Monoid instance (as with the writer monad); if a computation fails completely, the result is its record up to the point where it ended.

A more specific example would be something like parsing ill-formed input with the goal of extracting as much as you can and throwing out anything you can't interpret.