Safe Haskell | Safe-Inferred |
---|---|
Language | Haskell2010 |
A semigroup (Semigroup
) is a set with a binary associative operation
(<>
). This module defines a partial semigroup (PartialSemigroup
), a
semigroup for which <>
is not required to be defined over all inputs.
Synopsis
- class PartialSemigroup a where
- newtype AppendLeft a b = AppendLeft {
- unAppendLeft :: Either a b
- newtype AppendRight a b = AppendRight {
- unAppendRight :: Either a b
- groupAndConcat :: PartialSemigroup a => [a] -> [a]
- partialConcat :: PartialSemigroup a => [a] -> Maybe a
- partialConcat1 :: PartialSemigroup a => NonEmpty a -> Maybe a
- partialZip :: PartialSemigroup a => [a] -> [a] -> Maybe [a]
- partialZip1 :: PartialSemigroup a => NonEmpty a -> NonEmpty a -> Maybe (NonEmpty a)
- newtype Total a = Total {
- unTotal :: a
- newtype Partial a = Partial {}
- newtype One a = One {
- theOne :: a
- newtype AtMostOne a = AtMostOne {
- theOneMaybe :: Maybe a
Partial semigroup
class PartialSemigroup a where Source #
A PartialSemigroup
is like a Semigroup
, but with an operator returning
rather than Maybe
aa
.
For comparison:
(<>
) ::Semigroup
a => a -> a -> a (<>?
) ::PartialSemigroup
a => a -> a ->Maybe
a
The associativity axiom for partial semigroups
For all x
, y
, z
:
Relationship to the semigroup associativity axiom
The partial semigroup associativity axiom is a natural adaptation of the semigroup associativity axiom
x<>
(y<>
z) = (x<>
y)<>
z
with a slight modification to accommodate situations where <>
is undefined. We
may gain some insight into the connection between Semigroup
and
PartialSemigroup
by rephrasing the partial semigroup associativity in terms of
a partial <>
operator thusly:
For all x
, y
, z
:
Instances
Either
The exemplary nontrivial PartialSemigroup
is Either
, for which the append
operator produces a Just
result only if both arguments are Left
or both
arguments are Right
.
>>>
Left "ab" <>? Left "cd"
Just (Left "abcd")
>>>
Left "ab" <>? Right [1, 2]
Nothing
newtype AppendLeft a b Source #
A wrapper for Either
where the PartialSemigroup
operator is defined
only over Left
values.
Examples
>>>
AppendLeft (Left "ab") <>? AppendLeft (Left "cd")
Just (AppendLeft {unAppendLeft = Left "abcd"})
Anything else produces Nothing
>>>
AppendLeft (Right "ab") <>? AppendLeft (Right "cd")
Nothing
groupAndConcat
combines consecutive Left
values, leaving the Right
values
unmodified.
>>>
xs = [Left "a", Left "b", Right "c", Right "d", Left "e", Left "f"]
>>>
fmap unAppendLeft . groupAndConcat . fmap AppendLeft $ xs
[Left "ab",Right "c",Right "d",Left "ef"]
AppendLeft | |
|
Instances
newtype AppendRight a b Source #
A wrapper for Either
where the PartialSemigroup
operator is defined
only over Right
values.
Examples
>>>
AppendRight (Right "ab") <>? AppendRight (Right "cd")
Just (AppendRight {unAppendRight = Right "abcd"})
Anything else produces Nothing
>>>
AppendRight (Left "ab") <>? AppendRight (Left "cd")
Nothing
groupAndConcat
combines consecutive Right
values, leaving the Left
values
unmodified.
>>>
xs = [Left "a", Left "b", Right "c", Right "d", Left "e", Left "f"]
>>>
fmap unAppendRight . groupAndConcat . fmap AppendRight $ xs
[Left "a",Left "b",Right "cd",Left "e",Left "f"]
AppendRight | |
|
Instances
Tuples
A tuple forms a partial semigroups when all of its constituent parts have
partial semigroups. The append operator returns a Just
value when all of the
fields' append operators must return Just
values.
>>>
x = (Left "ab", Right "hi")
>>>
y = (Left "cd", Right "jk")
>>>
x <>? y
Just (Left "abcd",Right "hijk")
>>>
x = (Left "ab", Right "hi")
>>>
y = (Left "cd", Left "jk")
>>>
x <>? y
Nothing
Concatenation
groupAndConcat :: PartialSemigroup a => [a] -> [a] Source #
Apply a semigroup operation to any pairs of consecutive list elements where the semigroup operation is defined over them.
Examples
For Either
, groupAndConcat
combines contiguous sublists of Left
and
contiguous sublists of Right
.
>>>
xs = [Left "a", Right "b", Right "c", Left "d", Left "e", Left "f"]
>>>
groupAndConcat xs
[Left "a",Right "bc",Left "def"]
partialConcat :: PartialSemigroup a => [a] -> Maybe a Source #
If xs
is nonempty and the partial semigroup operator is defined for all
pairs of values in xs
, then
produces a partialConcat
xsJust
result with
the combination of all the values. Otherwise, returns Nothing
.
Examples
When all values can combine, we get a Just
of their combination.
>>>
partialConcat [Left "a", Left "b", Left "c"]
Just (Left "abc")
When some values cannot be combined, we get Nothing
.
>>>
partialConcat [Left "a", Left "b", Right "c"]
Nothing
When the list is empty, we get Nothing
.
>>>
partialConcat []
Nothing
partialConcat1 :: PartialSemigroup a => NonEmpty a -> Maybe a Source #
Like partialConcat
, but for non-empty lists.
Examples
When all values can combine, we get a Just
of their combination.
>>>
partialConcat1 (Left "a" :| [Left "b", Left "c"])
Just (Left "abc")
When some values cannot be combined, we get Nothing
.
>>>
partialConcat1 (Left "a" :| [Left "b", Right "c"])
Nothing
Zipping
partialZip :: PartialSemigroup a => [a] -> [a] -> Maybe [a] Source #
Examples
If lists are the same length and each pair of elements successfully, then we get
a Just
result.
>>>
xs = [Left "a", Left "b", Right "c"]
>>>
ys = [Left "1", Left "2", Right "3"]
>>>
partialZip xs ys
Just [Left "a1",Left "b2",Right "c3"]
If the pairs do not all combine, then we get Nothing
.
>>>
xs = [Left "a", Left "b", Right "c"]
>>>
ys = [Left "1", Right "2", Right "3"]
>>>
partialZip xs ys
Nothing
If the lists have different lengths, then we get Nothing
.
>>>
xs = [Left "a", Left "b", Right "c"]
>>>
ys = [Left "1", Left "2"]
>>>
partialZip xs ys
Nothing
partialZip1 :: PartialSemigroup a => NonEmpty a -> NonEmpty a -> Maybe (NonEmpty a) Source #
Like partialZip
, but for non-empty lists.
Examples
If lists are the same length and each pair of elements successfully, then we get
a Just
result.
>>>
xs = Left "a" :| [Left "b", Right "c"]
>>>
ys = Left "1" :| [Left "2", Right "3"]
>>>
partialZip1 xs ys
Just (Left "a1" :| [Left "b2",Right "c3"])
If the pairs do not all combine, then we get Nothing
.
>>>
xs = Left "a" :| [Left "b", Right "c"]
>>>
ys = Left "1" :| [Right "2", Right "3"]
>>>
partialZip1 xs ys
Nothing
If the lists have different lengths, then we get Nothing
.
>>>
xs = Left "a" :| [Left "b", Right "c"]
>>>
ys = Left "1" :| [Left "2"]
>>>
partialZip1 xs ys
Nothing
Total to partial
For every type with a Semigroup
, we can trivially construct a
PartialSemigroup
as:
x<>?
y =Just
(x<>
y)
Additionally, any type with a Semigroup
can be treated as a PartialSemigroup
by lifting it into Total
.
A wrapper to turn any value with a Semigroup
instance into a value with a
PartialSemigroup
instance whose <>?
operator always returns Just
.
Examples
>>>
Total "ab" <>? Total "cd"
Just (Total {unTotal = "abcd"})
>>>
f = getProduct . unTotal
>>>
g = Total . Product
>>>
fmap f . partialConcat . fmap g $ [1..4]
Just 24
Partial to total
For every type a
with a PartialSemigroup
, we can construct a total
Semigroup
for
as:Maybe
a
Just
x <>Just
y = x<>?
y _<>
_ =Nothing
We don't actually define this instance for Maybe
because it already has a
different Semigroup
defined over it, but we do provide the Partial
wrapper
which has this instance.
Refusing to combine
These are PartialSemigroup
instances that don't really combine their values
at all; whenever more than one thing is present, <>?
fails.
A partial semigroup operation which always fails.
AtMostOne | |
|
Instances
Eq a => Eq (AtMostOne a) Source # | |
Ord a => Ord (AtMostOne a) Source # | |
Defined in Data.PartialSemigroup | |
Read a => Read (AtMostOne a) Source # | |
Show a => Show (AtMostOne a) Source # | |
PartialSemigroup (AtMostOne a) Source # | |