packages feed

symantic-base-0.4.0.20211106: src/Symantic/Viewer.hs

{-# LANGUAGE GADTs #-} -- For Viewer
{-# LANGUAGE OverloadedStrings #-} -- For convenience
{-# LANGUAGE UndecidableInstances #-} -- For Show (SomeData a)
module Symantic.Viewer where

import Data.Int (Int)
import Data.String
import Text.Show
import qualified Data.Function as Fun
import qualified Prelude

import Symantic.Classes
import Symantic.Data
import Symantic.Derive
import Symantic.Fixity

data Viewer a where
  Viewer :: (ViewerEnv -> ShowS) -> Viewer a
  ViewerUnifix :: Unifix -> String -> String -> Viewer (a -> b)
  ViewerInfix :: Infix -> String -> String -> Viewer (a -> b -> c)
  ViewerApp :: Viewer (b -> a) -> Viewer b -> Viewer a

runViewer :: Viewer a -> ViewerEnv -> ShowS
runViewer (Viewer v) env = v env
runViewer (ViewerInfix _op name _infixName) _env = showString name
runViewer (ViewerUnifix _op name _unifixName) _env = showString name
runViewer (ViewerApp f x) env =
  pairViewer env op Fun.$
    runViewer f env{viewEnv_op = (op, SideL) } Fun..
    showString " " Fun..
    runViewer x env{viewEnv_op = (op, SideR) }
  where op = infixN 10

-- | Unusual, but enables to leverage default definition of methods.
type instance Derived Viewer = Viewer
instance LiftDerived Viewer where
  liftDerived = Fun.id

instance IsString (Viewer a) where
  fromString s = Viewer Fun.$ \_env -> showString s
instance Show (Viewer a) where
  showsPrec p = (`runViewer` ViewerEnv
    { viewEnv_op = (infixN p, SideL)
    , viewEnv_pair = pairParen
    , viewEnv_lamDepth = 1
    })
instance Show (SomeData Viewer a) where
  showsPrec p (SomeData x) = showsPrec p (derive x :: Viewer a)

data ViewerEnv
  = ViewerEnv
  { viewEnv_op :: (Infix, Side)
  , viewEnv_pair :: Pair
  , viewEnv_lamDepth :: Int
  }

pairViewer :: ViewerEnv -> Infix -> ShowS -> ShowS
pairViewer env op s =
  if isPairNeeded (viewEnv_op env) op
  then showString o Fun.. s Fun.. showString c
  else s
  where (o,c) = viewEnv_pair env

instance Abstractable Viewer where
  var = Fun.id
  lam f = viewLam "x" f
  lam1 f = viewLam "u" f
  ViewerInfix op _name infixName .@ ViewerApp x y = Viewer Fun.$ \env ->
    pairViewer env op Fun.$
      runViewer x env{viewEnv_op=(op, SideL)} Fun..
      showString " " Fun.. showString infixName Fun.. showString " " Fun..
      runViewer y env{viewEnv_op=(op, SideR)}
  ViewerInfix op name _infixName .@ x = Viewer Fun.$ \env ->
    showParen Prelude.True Fun.$
      runViewer x env{viewEnv_op=(op, SideL)} Fun..
      showString " " Fun.. showString name
  f .@ x = ViewerApp f x
viewLam :: String -> (Viewer a -> Viewer b) -> Viewer (a -> b)
viewLam varPrefix f = Viewer Fun.$ \env ->
  pairViewer env op Fun.$
    let x = showString varPrefix Fun..
            showsPrec 0 (viewEnv_lamDepth env) in
    -- showString "Lam1 (" .
    showString "\\" Fun.. x Fun.. showString " -> " Fun..
    runViewer (f (Viewer (\_env -> x))) env
      { viewEnv_op = (op, SideL)
      , viewEnv_lamDepth = Prelude.succ (viewEnv_lamDepth env)
      }
    -- . showString ")"
  where
  op = infixN 0
instance Anythingable Viewer
instance Bottomable Viewer where
  bottom = "<hidden>"
instance Show c => Constantable c Viewer where
  constant c = Viewer Fun.$ \_env -> shows c
instance Eitherable Viewer where
  left = "Left"
  right = "Right"
instance Equalable Viewer where
  equal = ViewerInfix (infixN 4) "(==)" "=="
instance Listable Viewer where
  cons = ViewerInfix (infixR 5) "(:)" ":"
  nil = "[]"
instance Maybeable Viewer where
  nothing = "Nothing"
  just = "Just"