packages feed

rerefined-0.2.0: src/Rerefined/Refine.hs

{-# LANGUAGE OverloadedStrings #-}

module Rerefined.Refine
  (
  -- * @Refined@
    type Refined
  , refine
  , unrefine

  -- * @Refined1@
  , type Refined1
  , refine1
  , unrefine1

  -- * Errors
  , type RefineFailure
  , prettyRefineFailure
  ) where

import Rerefined.Refined
import Rerefined.Refined1
import Rerefined.Predicate
import GHC.Exts ( proxy#, IsString )

-- | Refine @a@ with predicate @p@.
refine
    :: forall p a. Refine p a
    => a -> Either (RefineFailure String) (Refined p a)
refine a =
    case validate (proxy# @p) a of
      Nothing -> Right (Refined a)
      Just e  -> Left e

-- reifyPredicate is just a weaker version of validate without proxy.
-- Maybe the latter is useful, though...?

-- | Refine @f a@ with functor predicate @p@.
refine1
    :: forall p f a. Refine1 p f
    => f a -> Either (RefineFailure String) (Refined1 p f a)
refine1 fa =
    case validate1 (proxy# @p) fa of
      Nothing -> Right (Refined1 fa)
      Just e  -> Left e

-- TODO needs work. boring & idk how to format nicely. and extra \n at start
prettyRefineFailure :: (Semigroup a, IsString a) => RefineFailure a -> a
prettyRefineFailure = go (0 :: Int) . (\e -> [e])
  where
    go n = \case
      []     -> ""
      (e:es) ->
           "\n" <> indent n     <> refineFailurePredicate e
        <> "\n" <> indent (n+2) <> refineFailureDetail    e
        <> go (n+2) (refineFailureInner e)
        <> go n es
    indent = \case
      0 -> ""
      n -> " " <> indent (n-1)