packages feed

symantic-base-0.3.0.20211007: src/Symantic/View.hs

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

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

import Symantic.Class
import Symantic.Data
import Symantic.Derive
import Symantic.Fixity

data View a where
  View :: (ViewEnv -> ShowS) -> View a
  ViewUnifix :: Unifix -> String -> String -> View (a -> b)
  ViewInfix :: Infix -> String -> String -> View (a -> b -> c)
  ViewApp :: View (b -> a) -> View b -> View a

runView :: View a -> ViewEnv -> ShowS
runView (View v) env = v env
runView (ViewInfix _op name _infixName) _env = showString name
runView (ViewUnifix _op name _unifixName) _env = showString name
runView (ViewApp f x) env =
  pairView env op Fun.$
    runView f env{viewEnv_op = (op, SideL) } Fun..
    showString " " Fun..
    runView x env{viewEnv_op = (op, SideR) }
  where op = infixN 10

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

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

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

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