Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add printer combinators dual to parser combinators #71

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 70 additions & 2 deletions src/Data/Functor/Contravariant/Divisible.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE Safe #-}
Expand All @@ -15,9 +16,11 @@
module Data.Functor.Contravariant.Divisible
(
-- * Contravariant Applicative
Divisible(..), divided, conquered, liftD
Divisible(..), divided, conquered, liftD, (>*<), (>*), (*<)
-- * Contravariant Alternative
, Decidable(..), chosen, lost, (>*<)
, Decidable(..), chosen, lost
-- * Printer Combinators
, optionalD, manyD, many1D, sepByD, sepBy1D
-- * Mathematical definitions
-- ** Divisible
-- $divisible
Expand Down Expand Up @@ -50,6 +53,8 @@ import Data.Functor.Product
import Data.Functor.Reverse
import Data.Void

import Data.List (uncons)
import Data.List.NonEmpty(NonEmpty(..))
import Data.Monoid (Alt(..))

import Data.Proxy
Expand Down Expand Up @@ -603,3 +608,66 @@ instance Decidable SettableStateVar where
-- In addition, we expect the same kind of distributive law as is satisfied by the usual
-- covariant 'Alternative', w.r.t 'Applicative', which should be fully formulated and
-- added here at some point!

-- | Analagous to `(*>)`
--
-- @
-- showing :: 'Show' a => 'Op' 'String' a
-- showing = 'Op' 'show'
--
-- string :: String -> 'Op' 'String' ()
-- string = 'Op' '.' 'const'
--
-- greeting :: Show a => 'Op' 'String' a
-- greeting = string "Hello " '>*' showing
-- @
(>*) :: Divisible f => f () -> f a -> f a
(>*) = divide ((),)

infixr 5 >*

-- | Analagous to `(<*)`
--
-- @
-- emphatic :: 'Op' 'String' a -> 'Op' 'String' a
-- emphatic opstring = opstring '*<' string "!"
-- @
(*<) :: Divisible f => f a -> f () -> f a
(*<) = divide (,())

infixr 5 *<
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @gwils talk uses infixr 4. Not saying that one is correct, but is there a reason?

https://youtu.be/IJ_bVVsQhvc?t=1311

Copy link
Author

@echatav echatav Feb 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't seen this talk yet. My reasoning was by analogy. The Applicative operators are all infixl 4.

infixl 4 <*>
infixl 4 <*
infixl 4 *>

The Divisible operator so far is infixr 5 >*< so I gave >* and *< the same fixity. I'm not sure that's "correct".

Copy link
Author

@echatav echatav Feb 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like I switched the signatures of *< and >* from that talk too, oops.


mayhaps :: Maybe a -> Either () a
mayhaps m = case m of
Nothing -> Left ()
Just a -> Right a

-- | Zero or one.
optionalD :: Decidable f => f a -> f (Maybe a)
optionalD = choose mayhaps conquered

-- | Zero or more.
manyD :: Decidable f => f a -> f [a]
manyD p = choose (mayhaps . uncons) conquered (many1D' p)

-- Helper for `manyD` and `many1D`.
many1D' :: Decidable f => f a -> f (a,[a])
many1D' p = p >*< manyD p

-- | One or more.
many1D :: Decidable f => f a -> f (NonEmpty a)
many1D p = contramap (\(a:|as) -> (a,as)) (many1D' p)

-- | @'sepByD' sep p@ prints zero or more occurrences of @p@, separated by @sep@.
-- Consumes a list of values required by @p@.
sepByD :: Decidable f => f () -> f a -> f [a]
sepByD sep p = choose (mayhaps . uncons) conquered (sepBy1D' sep p)

-- Helper for `sepByD` and `sepBy1D`.
sepBy1D' :: Decidable f => f () -> f a -> f (a,[a])
sepBy1D' sep p = p >*< manyD (sep >* p)

-- | @'sepBy1D' sep p@ prints one or more occurrences of @p@, separated by @sep@.
-- Consumes a list of values required by @p@.
sepBy1D :: Decidable f => f () -> f a -> f (NonEmpty a)
sepBy1D sep p = contramap (\(a:|as) -> (a,as)) (sepBy1D' sep p)