MyNixOS website logo
Description

Tiny library to replace classic if/else.

Control.RichConditional

Typeclasses for describing "rich conditionals" which accomplish exactly what a classic if/else would but without the use of Bool.

The inspiration is found in dependently typed languages, in which a Bool is seen to be a comparatively pathetic datatype: it carries very little information. A useful test for some condition always introduces new information, but with a classical if/else, this information not known to the compiler because it's lossfully compressed into a Bool.

There is no new technology here, only typeclasses and functions which might make it more convenient to avoid using if/else.

#A motivating example

To demonstrate the point, suppose we defined the following module, in which the constructors of Visitor are not exposed:

module Visitor (
    Visitor
  , isLoggedIn
  , isGuest
  ) where

data Visitor = LoggedIn User | Guest

isLoggedIn :: Visitor -> Bool
isLoggedIn v = case v of
  LoggedIn _ -> True
  Guest -> False

isGuest :: Visitor -> Bool
isGuest v = case v of
  LoggedIn _ -> False
  Guest -> True

We have indicator functions on Visitor, but they don't allow for a useful interface. In the example below, we can't build a user's landing page because we can't get a hold of a User value.

userLandingPage :: User -> LandingPage

guestLandingPage :: LandingPage

blindLandingPage :: Visitor -> LandingPage
blindLandingPage v =
  if isLoggedIn v
  -- We know there's a user, but GHC does not!
  then userLandingPage ?
  else guestLandingPage

Evidently the Visitor library must provide some way to get a hold of a User from a Visitor, but if it does provide this, then why even bother giving the indicators isLoggedIn and isGuest?

Contrast the above definitions with a Bool-free approach:

module Visitor (
    Visitor
  , ifVisitor
  ) where

data Visitor = LoggedIn User | Guest

ifVisitor :: Visitor -> (User -> a) -> a -> a
ifVisitor v ifUser ifGuest = case v of
  LoggedIn user -> ifUser user
  Guest -> ifGuest

Visitor is just Maybe User with a new name, and ifVisitor is just the function maybe with its parameter order shuffled. It provides users of Visitor a way to get a hold of a User for LoggedIn cases without pattern matching on Visitor directly. It can be used to implement a well factored version of the landing page example:

userLandingPage :: User -> LandingPage

guestLandingPage :: LandingPage

landingPage :: Visitor -> LandingPage
landingPage v = ifVisitor v userLandingPage guestLandingPage

#Use of RichConditional

This modification of the above examples shows how RichConditional could be used:

{-# LANGUAGE MultiParamTypeClasses #-}

data User = User

data Guest = Human | Robot

data Visitor = LoggedIn User | NotLoggedIn Guest

instance PartialIf Visitor User where
  indicate v = case v of
    LoggedIn user -> Just user
    NotLoggedIn _ -> Nothing

instance PartialIf Visitor Guest where
  indicate v = case v of
    LoggedIn _ -> Nothing
    NotLoggedIn guest -> Just guest

instance TotalIf Visitor User Guest where
  decide v = case v of
    LoggedIn user -> Left user
    NotLoggedIn guest -> Right guest

allGuests :: [Visitor] -> [Guest]
allGuests vs = do
  v <- vs
  -- This is like guard, except that useful data comes
  -- out of it, so that it fulfills the type signature
  -- of allGuests.
  ensure v
Metadata

Version

0.1.0.0

License

Platforms (75)

    Darwin
    FreeBSD
    Genode
    GHCJS
    Linux
    MMIXware
    NetBSD
    none
    OpenBSD
    Redox
    Solaris
    WASI
    Windows
Show all
  • aarch64-darwin
  • aarch64-genode
  • aarch64-linux
  • aarch64-netbsd
  • aarch64-none
  • aarch64_be-none
  • arm-none
  • armv5tel-linux
  • armv6l-linux
  • armv6l-netbsd
  • armv6l-none
  • armv7a-darwin
  • armv7a-linux
  • armv7a-netbsd
  • armv7l-linux
  • armv7l-netbsd
  • avr-none
  • i686-cygwin
  • i686-darwin
  • i686-freebsd
  • i686-genode
  • i686-linux
  • i686-netbsd
  • i686-none
  • i686-openbsd
  • i686-windows
  • javascript-ghcjs
  • loongarch64-linux
  • m68k-linux
  • m68k-netbsd
  • m68k-none
  • microblaze-linux
  • microblaze-none
  • microblazeel-linux
  • microblazeel-none
  • mips-linux
  • mips-none
  • mips64-linux
  • mips64-none
  • mips64el-linux
  • mipsel-linux
  • mipsel-netbsd
  • mmix-mmixware
  • msp430-none
  • or1k-none
  • powerpc-netbsd
  • powerpc-none
  • powerpc64-linux
  • powerpc64le-linux
  • powerpcle-none
  • riscv32-linux
  • riscv32-netbsd
  • riscv32-none
  • riscv64-linux
  • riscv64-netbsd
  • riscv64-none
  • rx-none
  • s390-linux
  • s390-none
  • s390x-linux
  • s390x-none
  • vc4-none
  • wasm32-wasi
  • wasm64-wasi
  • x86_64-cygwin
  • x86_64-darwin
  • x86_64-freebsd
  • x86_64-genode
  • x86_64-linux
  • x86_64-netbsd
  • x86_64-none
  • x86_64-openbsd
  • x86_64-redox
  • x86_64-solaris
  • x86_64-windows