git @ Cat's Eye Technologies Lariat / master impl / Haskell / Data / Lariat.hs
master

Tree @master (Download .tar.gz)

Lariat.hs @masterraw · history · blame

-- Copyright (c) 2021-2023 Chris Pressey, Cat's Eye Technologies.
-- Distributed under a 2-clause BSD license.  For more details, see
-- LicenseRef-BSD-2-Clause-X-Lariat.txt in the LICENSES directory.
--
-- SPDX-License-Identifier: LicenseRef-BSD-2-Clause-X-Lariat

module Data.Lariat (name, var, app, abs, destruct) where

--
-- This is a concrete implementation of the Lariat ADT that uses a
-- "locally nameless"-like internal representation of lambda terms.
--

import Prelude hiding (abs)

class Freshable a where
    fresh :: [a] -> a

data Name = Name String
    deriving (Eq)

name s = Name s

instance Show Name where
    show (Name s) = s

instance Freshable Name where
    fresh names = expand $ longestName names (Name "")
        where
            expand (Name "") = Name "y"
            expand (Name (x:xs)) = Name (x:(x:xs))
            longestName [] acc = acc
            longestName (x:xs) acc = longestName xs (if (nameLength x) > (nameLength acc) then x else acc)
            nameLength (Name s) = length s

data Term a = FreeVar a
            | App (Term a) (Term a)
            | Abs (Term a)
            | BoundVar Integer
    deriving (Show)

var :: a -> Term a
var n = FreeVar n

app :: Term a -> Term a -> Term a
app t u = App t u

abs :: (Eq a) => a -> Term a -> Term a
abs n t = Abs (bind n t 0) where
    bind n (App t u) level = App (bind n t level) (bind n u level)
    bind n (Abs t) level = Abs (bind n t (level + 1))
    bind n t@(FreeVar m) level = if n == m then (BoundVar level) else t
    bind _ t _ = t

destruct :: (Freshable a) => Term a -> (a -> b) -> (Term a -> Term a -> b) -> (Term a -> a -> b) -> b
destruct (FreeVar n) f _ _ = f n
destruct (App t u)   _ f _ = f t u
destruct (Abs b)     _ _ f =
    let
        vars = freeVars b
        n = fresh vars
        u = unbind b (var n) 0
    in
        f u n
    where
        freeVars (FreeVar n) = [n]
        freeVars (App t u) = (freeVars t) ++ (freeVars u)
        freeVars (Abs b) = (freeVars b)
        freeVars _ = []

        unbind (App t u) x level = App (unbind t x level) (unbind u x level)
        unbind (Abs t) x level = Abs (unbind t x (level + 1))
        unbind t@(BoundVar i) x level = if i == level then x else t
        unbind t _ _ = t