Skip to content

Commit

Permalink
Merge pull request #361 from data61/alternative
Browse files Browse the repository at this point in the history
Add module on Alternative
  • Loading branch information
gwils authored Jul 30, 2019
2 parents 221327b + 23b6eb2 commit 3161c2a
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 0 deletions.
1 change: 1 addition & 0 deletions course.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ library
hs-source-dirs: src

exposed-modules: Course
Course.Alternative
Course.Anagrams
Course.Applicative
Course.Cheque
Expand Down
1 change: 1 addition & 0 deletions src/Course.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

module Course (module X) where

import Course.Alternative as X
import Course.Anagrams as X
import Course.Applicative as X
import Course.Cheque as X
Expand Down
179 changes: 179 additions & 0 deletions src/Course/Alternative.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE RebindableSyntax #-}

module Course.Alternative where

import Course.Applicative
import Course.Core
import Course.Functor
import Course.List
import Course.Optional
import Course.Parser
import qualified Prelude as P(fmap, return, (>>=))

-- | All instances of the `Alternative` type-class must satisfy three laws.
-- These laws are not checked by the compiler. These laws are given as:
--
-- * The law of left identity
-- `∀x. empty <|> x = x`
--
-- * The law of right identity
-- `∀x. x <|> empty = x`
--
-- * The law of associativity
-- `∀u v w. u <|> (v <|> w) = (u <|> v) <|> w`
--
-- You may notice that these are the same laws as Monoid. An alternative
-- can be considered a "monoid on applicative functors". The key difference
-- between the two classes is that Alternative is higher-kinded, meaning that
-- the type variable @k@ itself takes a type parameter.
-- The Alternative instance for @k@ is often distinct from any Monoid instance
-- for @k a@.
-- An Alternative instance should relate to the Applicative instance in some
-- way, although the exact relation required is an open question in the community.
-- Informally, it should be some kind of choice or alternation. Attempts to give
-- laws relating the Applicative and Alternative are discussed here:
-- https://wiki.haskell.org/Typeclassopedia#Laws_6
class Applicative k => Alternative k where
zero ::
k a
(<|>) ::
k a
-> k a
-> k a

infixl 3 <|>

-- | Return the first full Optional.
--
-- >>> Full 3 <|> zero
-- Full 3
--
-- >>> zero <|> Full 4
-- Full 4
--
-- >>> Full 3 <|> Full 4
-- Full 3
instance Alternative Optional where
zero ::
Optional a
zero =
error "todo: Course.Alternative zero#instance Optional"
(<|>) ::
Optional a
-> Optional a
-> Optional a
(<|>) =
error "todo: Course.Alternative (<|>)#instance Optional"

-- | Append the lists.
-- This instance views lists as a non-deterministic choice between elements,
-- so the way we "alternate" them is to append the lists.
--
-- >>> 3 :. 4 :. 5 :. Nil <|> Nil
-- [3,4,5]
--
-- >>> Nil <|> 6 :. 7 :. 8 :. Nil
-- [6,7,8]
--
-- >>> 3 :. 4 :. 5 :. Nil <|> 6 :. 7 :. 8 :. Nil
-- [3,4,5,6,7,8]
instance Alternative List where
zero ::
List a
zero =
error "todo: Course.Alternative zero#instance List"
(<|>) ::
List a
-> List a
-> List a
(<|>) =
error "todo: Course.Alternative (<|>)#instance List"

-- | Choose the first succeeding parser
--
-- /Tip:/ Check Parser.hs
--
-- >>> parse (character <|> valueParser 'v') ""
-- Result >< 'v'
--
-- >>> parse (constantParser UnexpectedEof <|> valueParser 'v') ""
-- Result >< 'v'
--
-- >>> parse (character <|> valueParser 'v') "abc"
-- Result >bc< 'a'
--
-- >>> parse (constantParser UnexpectedEof <|> valueParser 'v') "abc"
-- Result >abc< 'v'
instance Alternative Parser where
zero ::
Parser a
zero =
error "todo: Course.Alternative zero#instance Parser"
(<|>) ::
Parser a
-> Parser a
-> Parser a
(<|>) =
error "todo: Course.Alternative (<|>)#instance Parser"

-- | Run the provided Alternative action zero or more times, collecting
-- a list of the results.
--
-- /Tip:/ Use @some@, @pure@ and @(<|>)@.
--
-- >>> parse (many character) ""
-- Result >< ""
--
-- >>> parse (many digit) "123abc"
-- Result >abc< "123"
--
-- >>> parse (many digit) "abc"
-- Result >abc< ""
--
-- >>> parse (many character) "abc"
-- Result >< "abc"
--
-- >>> parse (many (character *> valueParser 'v')) "abc"
-- Result >< "vvv"
--
-- >>> parse (many (character *> valueParser 'v')) ""
-- Result >< ""
many :: Alternative k => k a -> k (List a)
many =
error "todo: Course.Alternative many"

-- | Run the provided Alternative action one or more times, collecting
-- a list of the results.
--
-- /Tip:/ Use @(:.)@ and @many@.
--
-- >>> parse (some (character)) "abc"
-- Result >< "abc"
--
-- >>> parse (some (character *> valueParser 'v')) "abc"
-- Result >< "vvv"
--
-- >>> isErrorResult (parse (some (character *> valueParser 'v')) "")
-- True
some :: Alternative k => k a -> k (List a)
some =
error "todo: Course.Alternative some"

-- | Combine a list of alternatives
--
-- >>> aconcat (Nil :: List (List Int))
-- []
--
-- >>> aconcat ((3:.4:.Nil) :. Nil :. (5:.6:.Nil) :. Nil
-- [3,4,5,6]

-- >>> aconcat (Empty :. Empty :. Full 7 :. Empty :. Full 8 :. Empty :. Nil)
-- Full 7
--
-- /Note:/ In the standard library, this function is called @asum@
aconcat :: Alternative k => List (k a) -> k a
aconcat =
error "todo: Course.Alternative aconcat"

0 comments on commit 3161c2a

Please sign in to comment.