Skip to content
Typeclasses standardizing operations dealing with datatypes representing booleans by their constructors
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
Combinator
proofs
LICENSE
README.md
Setup.hs
boolean-like.cabal
stack.yaml

README.md

A set of logical combinators under the typeclasses Andlike, Orlike, and Xorlike, defining operations dealing with boolean-representable structures. Maybe which has true-like Just and false-like Nothing, or [a] by true-like non-empty list and false-like empty list. It also introduces the optional Falsifier which is for unary false-like constructors, such as Nothing.

import Combinator.Booly

Operator precedence

The operators visually indicate precedence, which argument is to be returned when there is a conflict, by the direction of the LT/GT symbols. Xorlike does not have this issue because to succeed both cannot be true-like, and when both are false-like only unary false-like constructors are allowed, hence the argument precedence is irrelevant.

-- Or
Just 1 <|< Just 2 == Just 1 == Just 2 >|> Just 1
-- And
Left 1 <&< Left 2 == Left 1 == Left 2 >&> Left 1
-- Xor
Just 1 <^> Just 2 == Nothing == Just 2 <^> Just 1

Default typeclass signatures

Andlike and Orlike provide default instances for Applicative and Alternative respectively for convenience because there is a major overlap. However, not all Andlike and Orlike are also Applicative and Alternative respectively, and vice versa. Map from Data.Map is valid as Andlike, Orlike, and Xorlike, but does not have an Applicative instance, nor Alternative. There is an Applicative instance for ((->) a), but functions don't have an empty constructor, which make them invalid instances of all three boolean-like typeclasses. The same problems apply to Falsifier which has a default instance for Monoids by using mempty. Therefore, be careful when making use of the default instances for custom datatypes.

Isabelle proofs

Proofs are provided in proofs/Booly.thy over the Isabelle Maybe datatype equivalent.

Typeclasses and constraints

Andlike

class Andlike a where
    (<&<) :: a -> a -> a

    default (<&<) :: (Applicative f, f b ~ a) => a -> a -> a
    (<&<) = (<*)

Boolean-like logic operation <&< that acts like AND for any boolean-representable datatypes, e.g. [] or Maybe.

Associativity

(a <&< b) <&< c == a <&< (b <&< c)

Structural commutativity

a <&< b is structurally equivalent to b <&< a at the first construction layer.

Right 1 <&< Left 2  Right 2 <&< Left 1
Right _ <&< Left _  Right _ <&< Left _
Left _  Left _

Absorbing element / truth table

false <&< false == false
false <&< b == false
a <&< false == false
a <&< b == b

Orlike

class Orlike a where
    (<|<) :: a -> a -> a

    default (<|<) :: (Alternative f, f b ~ a) => a -> a -> a
    (<|<) = (<|>)

Boolean-like logic operation <|< that acts like OR for any boolean-representable datatypes, e.g. [a] or Maybe.

Associativity

(a <|< b) <|< c == a <|< (b <|< c)

Structural commutativity

a <|< b is structurally equivalent to b <|< a at the first construction layer.

Right 1 <|< Left 2  Right 2 <|< Left 1
Right _ <|< Left _  Right _ <|< Left _
Right _  Right _

Identity element / Truth table

false <|< false == false
false <|< b == b
a <|< false == a
a <|< b == a

Xorlike

class Xorlike a where
    (<^>) :: a -> a -> a

Boolean-like logic operation <^> that acts like XOR for any boolean-representable datatypes, e.g. [] or Maybe.

Structural associativity

a <^> (b <^> c) is structurally equivalent to (a <^> b) <^> c at the first construction layer.

Just 1 <^> (Just 2 <^> Just 3)  (Just 1 <^> Just 2) <^> Just 3
Just _ <^> (Just _ <^> Just _)  (Just _ <^> Just _) <^> Just _
Just _  Just _

Structural commutativity

a <^> b is structurally equivalent to b <^> a at the first construction layer.

Just 1 <^> Just 2  Just 2 <^> Just 1
Just _ <^> Just _  Just _ <^> Just _
Nothing  Nothing

Truth table

false <^> false == false
false <^> b == b
a <^> false == a
a <^> b == false

Falsifier

class Falsifier a where
    false :: a

    default false :: Monoid a => a
    false = mempty

The false-representing constructor value. Examples:

  • Maybe: Nothing
  • [a]: []
  • Text: empty

Helpful functions

(>&>) :: Andlike a => a -> a -> a

Flipped version of '<&<'. Returns the leftmost argument on both success or failure.

(>|>) :: Orlike a => a -> a -> a

Flipped version of '<|<'. Returns the leftmost argument on both success or failure.

andHead :: (Andlike a, Falsifier a, Foldable t) => t a -> a

Returns the first element on success of all values.

andLast :: (Andlike a, Falsifier a, Foldable t) => t a -> a

Returns the last element on success of all values.

andMappend :: (Andlike a, Monoid a) => a -> a -> a

Monadic append with the annihilating operator guarding each argument. Returns the mappended result on success.

andMconcat :: (Andlike a, Falsifier a, Monoid a, Foldable t) => t a -> a

Monadic concatenation with the annihilating operator guarding each argument.

isFalse :: (Eq a, Falsifier a) => a -> Bool
isTrue :: (Eq a, Falsifier a) => a -> Bool
boolF :: (Eq a, Eq b, Falsifier a, Falsifier b) => a -> a -> b -> a

Similar to 'Data.Bool.bool'

voidF :: Falsifier a => a -> a

Discard the argument and return 'false'.

whenF :: (Eq a, Eq b, Falsifier a, Falsifier b) => a -> b -> b

Similar to when but takes a boolean-like and returns false instead of ().

unlessF :: (Eq a, Eq b, Falsifier a, Falsifier b) => a -> b -> b

Similar to unless but takes a boolean-like and returns false instead of ().

Examples

Andlike

dealWithThis json = do
    let maybeMessage = decode json :: Maybe Message

    case maybeMessage of
        Just message -> do
            let maybeUser = user message
                maybeText = text message
            -- maybeText relies on maybeUser being a Just value.
            maybe (return ()) handleMsg (maybeUser >&> maybeText)

        Nothing -> return ()

Orlike

-- Attoparsec
bbcode :: Parser _
bbcode = do
    tagName <- openingTag
    -- Prioritize parsing bbcode, otherwise try text.
    contents <- text >|> bbcode
    closingTag tagName
    return contents

Xorlike

runMaybeT $ do
    -- Only one should succeed!
    msg <- maybeSuccess <^> maybeError
    liftIO $ sendToClient msg
You can’t perform that action at this time.