disco-0.2: src/Disco/Enumerate.hs
{-# LANGUAGE NondecreasingIndentation #-}
-- |
-- Module : Disco.Enumerate
-- Copyright : disco team and contributors
-- Maintainer : byorgey@gmail.com
--
-- SPDX-License-Identifier: BSD-3-Clause
--
-- Enumerate values inhabiting Disco types.
module Disco.Enumerate (
ValueEnumeration,
-- * Base types
enumVoid,
enumUnit,
enumBool,
enumN,
enumZ,
enumF,
enumQ,
enumC,
-- * Containers
enumSet,
-- , enumBag
enumList,
-- * Any type
enumType,
enumTypes,
-- * Lifted functions that return lists
enumerateType,
enumerateTypes,
)
where
import qualified Data.Enumeration.Invertible as E
import Disco.AST.Generic (Side (..))
import Disco.Types
import Disco.Value
type ValueEnumeration = E.IEnumeration Value
-- | Enumerate all values of type @Void@ (none).
enumVoid :: ValueEnumeration
enumVoid = E.void
-- | Enumerate all values of type @Unit@ (the single value @unit@).
enumUnit :: ValueEnumeration
enumUnit = E.singleton VUnit
-- | Enumerate the values of type @Bool@ as @[false, true]@.
enumBool :: ValueEnumeration
enumBool = E.mapE toV fromV $ E.finiteList [L, R]
where
toV i = VInj i VUnit
fromV (VInj i VUnit) = i
fromV _ = error "enumBool.fromV: value isn't a bool"
-- | Enumerate all values of type @Nat@ (0, 1, 2, ...).
enumN :: ValueEnumeration
enumN = E.mapE (ratv . fromInteger) (floor . vrat) E.nat
-- | Enumerate all values of type @Integer@ (0, 1, -1, 2, -2, ...).
enumZ :: ValueEnumeration
enumZ = E.mapE (ratv . fromInteger) (floor . vrat) E.int
-- | Enumerate all values of type @Fractional@ in the Calkin-Wilf
-- order (1, 1/2, 2, 1/3, 3/2, 2/3, 3, ...).
enumF :: ValueEnumeration
enumF = E.mapE ratv vrat E.cw
-- | Enumerate all values of type @Rational@ in the Calkin-Wilf order,
-- with negatives interleaved (0, 1, -1, 1/2, -1/2, 2, -2, ...).
enumQ :: ValueEnumeration
enumQ = E.mapE ratv vrat E.rat
-- | Enumerate all Unicode characters.
enumC :: ValueEnumeration
enumC = E.mapE toV fromV (E.boundedEnum @Char)
where
toV = ratv . fromIntegral . fromEnum
fromV = toEnum . floor . vrat
-- | Enumerate all *finite* sets over a certain element type, given an
-- enumeration of the elements. If we think of each finite set as a
-- binary string indicating which elements in the enumeration are
-- members, the sets are enumerated in order of the binary strings.
enumSet :: ValueEnumeration -> ValueEnumeration
enumSet e = E.mapE toV fromV (E.finiteSubsetOf e)
where
toV = VBag . map (,1)
fromV (VBag vs) = map fst vs
fromV _ = error "enumSet.fromV: value isn't a set"
-- | Enumerate all *finite* lists over a certain element type, given
-- an enumeration of the elements. It is very difficult to describe
-- the order in which the lists are generated.
enumList :: ValueEnumeration -> ValueEnumeration
enumList e = E.mapE toV fromV (E.listOf e)
where
toV = foldr VCons VNil
fromV (VCons h t) = h : fromV t
fromV VNil = []
fromV _ = error "enumList.fromV: value isn't a list"
-- | Enumerate all functions from a finite domain, given enumerations
-- for the domain and codomain.
enumFunction :: ValueEnumeration -> ValueEnumeration -> ValueEnumeration
enumFunction xs ys =
case (E.card xs, E.card ys) of
(E.Finite 0, _) -> E.singleton (VFun $ \_ -> error "enumFunction: void function called")
(_, E.Finite 0) -> E.void
(_, E.Finite 1) -> E.singleton (VFun $ \_ -> E.select ys 0)
_ -> E.mapE toV fromV (E.functionOf xs ys)
where
-- XXX TODO: better error message on functions with an infinite domain
toV = VFun
fromV (VFun f) = f
fromV _ = error "enumFunction.fromV: value isn't a VFun"
-- | Enumerate all values of a product type, given enumerations of the
-- two component types. Uses a fair interleaving for infinite
-- component types.
enumProd :: ValueEnumeration -> ValueEnumeration -> ValueEnumeration
enumProd xs ys = E.mapE toV fromV $ (E.><) xs ys
where
toV (x, y) = VPair x y
fromV (VPair x y) = (x, y)
fromV _ = error "enumProd.fromV: value isn't a pair"
-- | Enumerate all values of a sum type, given enumerations of the two
-- component types.
enumSum :: ValueEnumeration -> ValueEnumeration -> ValueEnumeration
enumSum xs ys = E.mapE toV fromV $ (E.<+>) xs ys
where
toV (Left x) = VInj L x
toV (Right y) = VInj R y
fromV (VInj L x) = Left x
fromV (VInj R y) = Right y
fromV _ = error "enumSum.fromV: value isn't a sum"
-- | Enumerate the values of a given type.
enumType :: Type -> ValueEnumeration
enumType TyVoid = enumVoid
enumType TyUnit = enumUnit
enumType TyBool = enumBool
enumType TyN = enumN
enumType TyZ = enumZ
enumType TyF = enumF
enumType TyQ = enumQ
enumType TyC = enumC
enumType (TySet t) = enumSet (enumType t)
enumType (TyList t) = enumList (enumType t)
enumType (a :*: b) = enumProd (enumType a) (enumType b)
enumType (a :+: b) = enumSum (enumType a) (enumType b)
enumType (a :->: b) = enumFunction (enumType a) (enumType b)
enumType ty = error $ "enumType: can't enumerate " ++ show ty
-- | Enumerate a finite product of types.
enumTypes :: [Type] -> E.IEnumeration [Value]
enumTypes [] = E.singleton []
enumTypes (t : ts) = E.mapE toL fromL $ (E.><) (enumType t) (enumTypes ts)
where
toL (x, xs) = x : xs
fromL (x : xs) = (x, xs)
fromL [] = error "enumTypes.fromL: empty list not in enumeration range"
-- | Produce an actual list of the values of a type.
enumerateType :: Type -> [Value]
enumerateType = E.enumerate . enumType
-- | Produce an actual list of values enumerated from a finite product
-- of types.
enumerateTypes :: [Type] -> [[Value]]
enumerateTypes = E.enumerate . enumTypes