choice (empty) → 0.1.0.0
raw patch · 4 files changed
+144/−0 lines, 4 filesdep +basesetup-changed
Dependencies added: base
Files
- README.md +1/−0
- Setup.hs +2/−0
- choice.cabal +22/−0
- src/Data/Choice.hs +119/−0
+ README.md view
@@ -0,0 +1,1 @@+# Choice: a solution to boolean blindness
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ choice.cabal view
@@ -0,0 +1,22 @@+name: choice+version: 0.1.0.0+synopsis: A solution to boolean blindness.+description: Please see README.md.+homepage: https://github.com/mboes/choice#readme+license: PublicDomain+author: Mathieu Boespflug+maintainer: m@tweag.io+category: Data+build-type: Simple+extra-source-files: README.md+cabal-version: >=1.10++library+ hs-source-dirs: src+ exposed-modules: Data.Choice+ build-depends: base >= 4.7 && < 5+ default-language: Haskell2010++source-repository head+ type: git+ location: https://github.com/mboes/choice
+ src/Data/Choice.hs view
@@ -0,0 +1,119 @@+-- | Represent do\/don't, is\/isn't, with\/without flags with 'Choice'.+--+-- <https://existentialtype.wordpress.com/2011/03/15/boolean-blindness/ Boolean blindness>+-- refers to the problem that boolean literals on their own aren't very+-- informative. In any given context, what does 'True' mean? What does 'False'+-- mean? Instead of passing arguments of type 'Bool' to functions, consider+-- using 'Choice'.+--+-- 'Choice' is the type of labeled booleans. Use it as follows:+--+-- @+-- {-\# LANGUAGE OverloadedLabels \#-}+--+-- import Data.Choice (Choice, Do, Don't)+--+-- -- Blocking read: block until N bytes available.+-- -- Non-blocking: return as many bytes as are available.+-- readBytes :: Handle -> Choice "block" -> Int -> IO ByteString+-- readBytes = ...+--+-- action1 = print =<< readBytes h (Don't #block) 1024+-- @+--+-- For GHC < 8.0, where overloaded labels are not available, substitute+-- @(Label :: Label "block")@ for @#block@.+--+-- __A comment on labels:__ why use labels? We could as well ask the user to+-- define ad hoc constructors. But unlike constructors, labels are guaranteed to+-- be singletons. That's exactly what we want: a label doesn't carry any runtime+-- information, it's just a type annotation. Better yet, with labels, there is+-- no need to ensure that constructor names are unique, nor to pollute the+-- precious constructor namespace in a large module with many flags.++{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE TypeFamilies #-}++{-# OPTIONS_GHC -fno-warn-missing-pattern-synonym-signatures #-}++module Data.Choice+ ( Choice+ -- * Conversion+ , fromBool+ , toBool+ -- * Choice aliases+ , pattern Do+ , pattern Don't+ , pattern Is+ , pattern Isn't+ , pattern With+ , pattern Without+ -- * Internal+ -- $label-export+ , Label(..)+ ) where++import GHC.OverloadedLabels (IsLabel(..))+import GHC.TypeLits (Symbol)++-- $label-export+--+-- The 'Label' data type is only exported in full for compatibility with+-- versions of GHC older than 8.0.++-- | A synonym for 'Data.Proxy.Proxy'.+data Label (a :: Symbol) = Label deriving (Eq, Ord, Show)++instance x ~ x' => IsLabel x (Label x') where+ fromLabel _ = Label++-- | A labeled boolean choice.+data Choice (a :: Symbol)+ = Off {-# UNPACK #-} !(Label a)+ | On {-# UNPACK #-} !(Label a)+ deriving (Eq, Ord)++instance Show (Choice a) where+ show x = "fromBool " ++ show (toBool x)++-- | Alias for 'True', e.g. @Do #block@.+pattern Do x = On x++-- | Alias for 'False', e.g. @Don't #block@.+pattern Don't x = Off x++-- | Alias for 'True', e.g. @Is #ordered@.+pattern Is x = On x++-- | Alias for 'False', e.g. @Isn't #ordered@.+pattern Isn't x = Off x++-- | Alias for 'True', e.g. @With #ownDirectory@.+pattern With x = On x++-- | Alias for 'False', e.g. @Without #ownDirectory@.+pattern Without x = Off x++instance Enum (Choice a) where+ toEnum 0 = Off Label+ toEnum 1 = On Label+ toEnum _ = error "Prelude.Enum.Choice.toEnum: bad argument"++ fromEnum (Off _) = 0+ fromEnum (On _) = 1++instance Bounded (Choice a) where+ minBound = Off Label+ maxBound = On Label++toBool :: Choice a -> Bool+toBool (Off _) = False+toBool (On _) = True++fromBool :: Bool -> Choice a+fromBool False = Off Label+fromBool True = On Label