Copyright | (c) Justin Le 2021 |
---|---|
License | BSD3 |
Maintainer | justin@jle.im |
Stability | experimental |
Portability | non-portable |
Safe Haskell | Safe-Inferred |
Language | Haskell2010 |
Synopsis
- class Invariant f => Inalt f where
- class Inalt f => Inplus f where
- class (Inplus f, Inplicative f) => Internative f
- swervedN :: Inplus f => NP f as -> f (NS I as)
- swervedNMap :: Inplus f => (NS I as -> b) -> (b -> NS I as) -> NP f as -> f b
- swervedN1 :: Inalt f => NP f (a ': as) -> f (NS I (a ': as))
- swervedN1Map :: Inalt f => (NS I (a ': as) -> b) -> (b -> NS I (a ': as)) -> NP f (a ': as) -> f b
Typeclass
class Invariant f => Inalt f where Source #
The invariant counterpart of Alt
and Decide
.
Conceptually you can think of Alt
as, given a way to "inject" a
and
b
as c
, lets you merge f a
(producer of a
) and f b
(producer
of b
) into a f c
(producer of c
), in an "either-or" fashion.
Decide
can be thought of as, given a way to "discriminate" a c
as
either a a
or a b
, lets you merge f a
(consumer of a
) and f b
(consumder of b
) into a f c
(consumer of c
) in an "either-or"
forking fashion (split the c
into a
or b
, and use the appropriate
handler).
Inalt
, for swerve
, requires both an injecting function and
a choosing function in order to merge f b
(producer and consumer of
b
) and f c
(producer and consumer of c
) into a f a
in an
either-or manner. You can think of it as, for the f a
, it "chooses"
if the a
is actually a b
or a c
with the a ->
,
feeds it to either the original Either
b cf b
or the original f c
, and then
re-injects the output back into a a
with the b -> a
or the c -> a
.
Since: 0.4.0.0
swerve :: (b -> a) -> (c -> a) -> (a -> Either b c) -> f b -> f c -> f a Source #
Like <!>
, decide
, or choose
, but requires both
an injecting and a choosing function.
It is used to merge f b
(producer and consumer of b
) and f c
(producer and consumer of c
) into a f a
in an either-or manner.
You can think of it as, for the f a
, it "chooses" if the a
is
actually a b
or a c
with the a ->
, feeds it to
either the original Either
b cf b
or the original f c
, and then re-injects
the output back into a a
with the b -> a
or the c -> a
.
An important property is that it will only ever use exactly one
of
the options given in order to fulfil its job. If you swerve an f
a
and an f b
into an f c
, in order to consume/produdce the c
,
it will only use either the f a
or the f b
-- exactly one of
them.
Since: 0.4.0.0
Instances
class Inalt f => Inplus f where Source #
The invariant counterpart of Alt
and Conclude
.
The main important action is described in Inalt
, but this adds reject
,
which is the counterpart to empty
and conclude
and conquer
. It's the identity to
swerve
; if combine two f a
s with swerve
, and one of them is
reject
, then that banch will never be taken.
Conceptually, if you think of swerve
as "choosing one path and
re-injecting back", then reject
introduces a branch that is impossible
to take.
Instances
class (Inplus f, Inplicative f) => Internative f Source #
The invariant counterpart to Alternative
and Decidable
: represents
a combination of both Applicative
and Alt
, or Divisible
and
Conclude
. There are laws?
Instances
Assembling Helpers
swervedN :: Inplus f => NP f as -> f (NS I as) Source #
Convenient wrapper to build up an Inplus
instance on by providing
each branch of it. This makes it much easier to build up longer chains
because you would only need to write the splitting/joining functions in
one place.
For example, if you had a data type
data MyType = MTI Int | MTB Bool | MTS String
and an invariant functor and Inplus
instance Prim
(representing, say,
a bidirectional parser, where Prim Int
is a bidirectional parser for
an Int
), then you could assemble a bidirectional parser for
a
MyType@ using:
invmap (case MTI x -> Z (I x); MTB y -> S (Z (I y)); MTS z -> S (S (Z (I z)))) (case Z (I x) -> MTI x; S (Z (I y)) -> MTB y; S (S (Z (I z))) -> MTS z) $ swervedN $ intPrim :* boolPrim :* stringPrim :* Nil
Some notes on usefulness depending on how many components you have:
- If you have 0 components, use
reject
directly. - If you have 1 component, use
inject
directly. - If you have 2 components, use
swerve
directly. - If you have 3 or more components, these combinators may be useful; otherwise you'd need to manually peel off eithers one-by-one.
Since: 0.4.1.0
swervedNMap :: Inplus f => (NS I as -> b) -> (b -> NS I as) -> NP f as -> f b Source #
Given a function to "discern out" a data type into possible NS
(multi-way Either) branches and one to re-assemble each brann, swerve
all of the components together.
For example, if you had a data type
data MyType = MTI Int | MTB Bool | MTS String
and an invariant functor and Inplus
instance Prim
(representing, say,
a bidirectional parser, where Prim Int
is a bidirectional parser for
an Int
), then you could assemble a bidirectional parser for
a
MyType@ using:
swervedNMap (case MTI x -> Z (I x); MTB y -> S (Z (I y)); MTS z -> S (S (Z (I z)))) (case Z (I x) -> MTI x; S (Z (I y)) -> MTB y; S (S (Z (I z))) -> MTS z) $ $ intPrim :* boolPrim :* stringPrim :* Nil
Some notes on usefulness depending on how many components you have:
- If you have 0 components, use
reject
directly. - If you have 1 component, you don't need anything.
- If you have 2 components, use
swerve
directly. - If you have 3 or more components, these combinators may be useful; otherwise you'd need to manually peel off eithers one-by-one.
See notes on swervedNMap
for more details and caveats.
Since: 0.4.1.0