Edit reference semantics for style and update PDF

This commit is contained in:
Rob Dockins 2020-08-20 18:45:49 -07:00
parent 4dec92ee1f
commit 04bd63380e
2 changed files with 118 additions and 60 deletions

Binary file not shown.

View File

@ -106,7 +106,8 @@ functions, unless they represent computations under *E*.
*M*(`Bit`) is a discrete cpo with values for `True`, `False`, which *M*(`Bit`) is a discrete cpo with values for `True`, `False`, which
we simply represent in Haskell as `Bool`. we simply represent in Haskell as `Bool`.
Similarly, *M*(`Integer`) is a discrete cpo with values for integers, Similarly, *M*(`Integer`) is a discrete cpo with values for integers,
which we model as Haskell's `Integer`. which we model as Haskell's `Integer`. Likewise with the other
base types.
The value cpos for lists, tuples, and records are cartesian products The value cpos for lists, tuples, and records are cartesian products
of _computations_. For example *M*(`(a,b)`) = *E*(*M*(`a`)) × *E*(*M*(`b`)). of _computations_. For example *M*(`(a,b)`) = *E*(*M*(`a`)) × *E*(*M*(`b`)).
@ -280,28 +281,39 @@ assigns values to those variables.
> evalExpr env expr = > evalExpr env expr =
> case expr of > case expr of
> >
> EList es _ty -> pure $ VList (Nat (genericLength es)) [ evalExpr env e | e <- es ] > EList es _ty ->
> ETuple es -> pure $ VTuple [ evalExpr env e | e <- es ] > pure $ VList (Nat (genericLength es)) [ evalExpr env e | e <- es ]
> ERec fields -> pure $ VRecord [ (f, evalExpr env e) | (f, e) <- canonicalFields fields ] >
> ESel e sel -> evalSel sel =<< evalExpr env e > ETuple es ->
> ESet ty e sel v -> evalSet (evalValType (envTypes env) ty) > pure $ VTuple [ evalExpr env e | e <- es ]
> (evalExpr env e) sel (evalExpr env v) >
> ERec fields ->
> pure $ VRecord [ (f, evalExpr env e) | (f, e) <- canonicalFields fields ]
>
> ESel e sel ->
> evalSel sel =<< evalExpr env e
>
> ESet ty e sel v ->
> evalSet (evalValType (envTypes env) ty)
> (evalExpr env e) sel (evalExpr env v)
> >
> EIf c t f -> > EIf c t f ->
> condValue (fromVBit <$> evalExpr env c) (evalExpr env t) (evalExpr env f) > condValue (fromVBit <$> evalExpr env c) (evalExpr env t) (evalExpr env f)
> >
> EComp _n _ty e branches -> > EComp _n _ty e branches -> evalComp env e branches
> evalComp env e branches
> >
> EVar n -> > EVar n ->
> case Map.lookup n (envVars env) of > case Map.lookup n (envVars env) of
> Just val -> val > Just val -> val
> Nothing -> evalPanic "evalExpr" ["var `" ++ show (pp n) ++ "` is not defined" ] > Nothing ->
> evalPanic "evalExpr" ["var `" ++ show (pp n) ++ "` is not defined" ]
> >
> ETAbs tv b -> > ETAbs tv b ->
> case tpKind tv of > case tpKind tv of
> KType -> pure $ VPoly $ \ty -> evalExpr (bindType (tpVar tv) (Right ty) env) b > KType -> pure $ VPoly $ \ty ->
> KNum -> pure $ VNumPoly $ \n -> evalExpr (bindType (tpVar tv) (Left n) env) b > evalExpr (bindType (tpVar tv) (Right ty) env) b
> KNum -> pure $ VNumPoly $ \n ->
> evalExpr (bindType (tpVar tv) (Left n) env) b
> k -> evalPanic "evalExpr" ["Invalid kind on type abstraction", show k] > k -> evalPanic "evalExpr" ["Invalid kind on type abstraction", show k]
> >
> ETApp e ty -> > ETApp e ty ->
@ -357,7 +369,7 @@ Update the given value using the given selector and new value.
> case (tyv, sel) of > case (tyv, sel) of
> (TVTuple ts, TupleSel n _) -> updTupleAt ts n > (TVTuple ts, TupleSel n _) -> updTupleAt ts n
> (TVRec fs, RecordSel n _) -> updRecAt fs n > (TVRec fs, RecordSel n _) -> updRecAt fs n
> (TVSeq len _, ListSel n _) -> updSeqAt len n > (TVSeq len _, ListSel n _) -> updSeqAt len n
> (_, _) -> evalPanic "evalSet" ["type/selector mismatch", show tyv, show sel] > (_, _) -> evalPanic "evalSet" ["type/selector mismatch", show tyv, show sel]
> where > where
> updTupleAt ts n = > updTupleAt ts n =
@ -606,20 +618,20 @@ by corresponding type classes:
> >
> -- Round > -- Round
> , "floor" ~> unary (roundUnary floor > , "floor" ~> unary (roundUnary floor
> (eitherToE . FP.floatToInteger "floor" FP.ToNegInf) > (eitherToE . FP.floatToInteger "floor" FP.ToNegInf))
> ) >
> , "ceiling" ~> unary (roundUnary ceiling > , "ceiling" ~> unary (roundUnary ceiling
> (eitherToE . FP.floatToInteger "ceiling" FP.ToPosInf) > (eitherToE . FP.floatToInteger "ceiling" FP.ToPosInf))
> ) >
> , "trunc" ~> unary (roundUnary truncate > , "trunc" ~> unary (roundUnary truncate
> (eitherToE . FP.floatToInteger "trunc" FP.ToZero) > (eitherToE . FP.floatToInteger "trunc" FP.ToZero))
> ) >
> , "roundAway" ~> unary (roundUnary roundAwayRat > , "roundAway" ~> unary (roundUnary roundAwayRat
> (eitherToE . FP.floatToInteger "roundAway" FP.Away) > (eitherToE . FP.floatToInteger "roundAway" FP.Away))
> ) >
> , "roundToEven"~> unary (roundUnary round > , "roundToEven"~> unary (roundUnary round
> (eitherToE . FP.floatToInteger "roundToEven" FP.NearEven) > (eitherToE . FP.floatToInteger "roundToEven" FP.NearEven))
> ) >
> >
> -- Comparison > -- Comparison
> , "<" ~> binary (cmpOrder (\o -> o == LT)) > , "<" ~> binary (cmpOrder (\o -> o == LT))
@ -634,11 +646,15 @@ by corresponding type classes:
> , "/$" ~> vFinPoly $ \n -> pure $ > , "/$" ~> vFinPoly $ \n -> pure $
> VFun $ \l -> pure $ > VFun $ \l -> pure $
> VFun $ \r -> > VFun $ \r ->
> vWord n <$> appOp2 divWrap (fromSignedVWord =<< l) (fromSignedVWord =<< r) > vWord n <$> appOp2 divWrap
> (fromSignedVWord =<< l)
> (fromSignedVWord =<< r)
> , "%$" ~> vFinPoly $ \n -> pure $ > , "%$" ~> vFinPoly $ \n -> pure $
> VFun $ \l -> pure $ > VFun $ \l -> pure $
> VFun $ \r -> > VFun $ \r ->
> vWord n <$> appOp2 modWrap (fromSignedVWord =<< l) (fromSignedVWord =<< r) > vWord n <$> appOp2 modWrap
> (fromSignedVWord =<< l)
> (fromSignedVWord =<< r)
> , ">>$" ~> signedShiftRV > , ">>$" ~> signedShiftRV
> , "lg2" ~> vFinPoly $ \n -> pure $ > , "lg2" ~> vFinPoly $ \n -> pure $
> VFun $ \v -> > VFun $ \v ->
@ -646,7 +662,9 @@ by corresponding type classes:
> -- Rational > -- Rational
> , "ratio" ~> VFun $ \l -> pure $ > , "ratio" ~> VFun $ \l -> pure $
> VFun $ \r -> > VFun $ \r ->
> VRational <$> (appOp2 ratioOp (fromVInteger <$> l) (fromVInteger <$> r)) > VRational <$> appOp2 ratioOp
> (fromVInteger <$> l)
> (fromVInteger <$> r)
> >
> -- Z n > -- Z n
> , "fromZ" ~> vFinPoly $ \n -> pure $ > , "fromZ" ~> vFinPoly $ \n -> pure $
@ -740,7 +758,8 @@ by corresponding type classes:
> VPoly $ \ty -> pure $ > VPoly $ \ty -> pure $
> vFinPoly $ \len -> > vFinPoly $ \len ->
> let f i = literal i ty > let f i = literal i ty
> in pure (VList (Nat len) (map f (genericTake len [first, next ..]))) > in pure (VList (Nat len)
> (map f (genericTake len [first, next ..])))
> >
> , "infFrom" ~> VPoly $ \ty -> pure $ > , "infFrom" ~> VPoly $ \ty -> pure $
> VFun $ \first -> > VFun $ \first ->
@ -839,7 +858,8 @@ error if any of the input bits contain an evaluation error.
> >
> -- | Convert a list of booleans in signed big-endian format to an integer. > -- | Convert a list of booleans in signed big-endian format to an integer.
> signedBitsToInteger :: [Bool] -> Integer > signedBitsToInteger :: [Bool] -> Integer
> signedBitsToInteger [] = evalPanic "signedBitsToInteger" ["Bitvector has zero length"] > signedBitsToInteger [] =
> evalPanic "signedBitsToInteger" ["Bitvector has zero length"]
> signedBitsToInteger (b0 : bs) = foldl f (if b0 then -1 else 0) bs > signedBitsToInteger (b0 : bs) = foldl f (if b0 then -1 else 0) bs
> where f x b = if b then 2 * x + 1 else 2 * x > where f x b = if b then 2 * x + 1 else 2 * x
@ -847,8 +867,13 @@ Function `vWord` converts an integer back to the big-endian bitvector
representation. representation.
> vWord :: Integer -> Integer -> Value > vWord :: Integer -> Integer -> Value
> vWord w e = VList (Nat w) [ pure (VBit (testBit e (fromInteger i))) | i <- [w-1, w-2 .. 0] ] > vWord w e
> -- TODO: this is a dangerous `fromInteger` that targets Haskell type Int > | w > toInteger (maxBound :: Int) =
> evalPanic "vWord" ["Word length too large", show w]
> | otherwise =
> VList (Nat w) [ mkBit i | i <- [w-1, w-2 .. 0 ] ]
> where
> mkBit i = pure (VBit (testBit e (fromInteger i)))
Errors Errors
------ ------
@ -877,7 +902,8 @@ For functions, `zero` returns the constant function that returns
> zero (TVSeq n ety) = VList (Nat n) (genericReplicate n (pure (zero ety))) > zero (TVSeq n ety) = VList (Nat n) (genericReplicate n (pure (zero ety)))
> zero (TVStream ety) = VList Inf (repeat (pure (zero ety))) > zero (TVStream ety) = VList Inf (repeat (pure (zero ety)))
> zero (TVTuple tys) = VTuple (map (pure . zero) tys) > zero (TVTuple tys) = VTuple (map (pure . zero) tys)
> zero (TVRec fields) = VRecord [ (f, pure (zero fty)) | (f, fty) <- canonicalFields fields ] > zero (TVRec fields) = VRecord [ (f, pure (zero fty))
> | (f, fty) <- canonicalFields fields ]
> zero (TVFun _ bty) = VFun (\_ -> pure (zero bty)) > zero (TVFun _ bty) = VFun (\_ -> pure (zero bty))
> zero (TVAbstract{}) = evalPanic "zero" ["Abstract type not in `Zero`"] > zero (TVAbstract{}) = evalPanic "zero" ["Abstract type not in `Zero`"]
@ -894,7 +920,8 @@ Given a literal integer, construct a value of a type that can represent that lit
> go TVRational = pure (VRational (fromInteger i)) > go TVRational = pure (VRational (fromInteger i))
> go (TVIntMod n) > go (TVIntMod n)
> | i < n = pure (VInteger i) > | i < n = pure (VInteger i)
> | otherwise = evalPanic "literal" ["Literal out of range for type Z " ++ show n] > | otherwise = evalPanic "literal"
> ["Literal out of range for type Z " ++ show n]
> go (TVSeq w a) > go (TVSeq w a)
> | isTBit a = pure (vWord w i) > | isTBit a = pure (vWord w i)
> go ty = evalPanic "literal" [show ty ++ " cannot represent literals"] > go ty = evalPanic "literal" [show ty ++ " cannot represent literals"]
@ -905,6 +932,7 @@ The rounding flag determines the behavior if the literal cannot be represented
exactly: 0 means report and error, other numbers round to the nearest exactly: 0 means report and error, other numbers round to the nearest
representable value. representable value.
> -- TODO: we should probably be using the rounding mode here...
> fraction :: Integer -> Integer -> Integer -> TValue -> E Value > fraction :: Integer -> Integer -> Integer -> TValue -> E Value
> fraction top btm _rnd ty = > fraction top btm _rnd ty =
> case ty of > case ty of
@ -920,8 +948,8 @@ Logic
----- -----
Bitwise logic primitives are defined by recursion over the type Bitwise logic primitives are defined by recursion over the type
structure. On type `Bit`, we use `fmap` and `liftA2` to make these structure. On type `Bit`, the operations are strict in all
operations strict in all arguments. For example, `True || error "foo"` arguments. For example, `True || error "foo"`
does not evaluate to `True`, but yields a run-time exception. On other does not evaluate to `True`, but yields a run-time exception. On other
types, run-time exceptions on input bits only affect the output bits types, run-time exceptions on input bits only affect the output bits
at the same positions. at the same positions.
@ -938,7 +966,8 @@ at the same positions.
> TVTuple etys -> VTuple . zipWith go etys . fromVTuple <$> val > TVTuple etys -> VTuple . zipWith go etys . fromVTuple <$> val
> TVRec fields -> > TVRec fields ->
> do val' <- val > do val' <- val
> pure $ VRecord [ (f, go fty (lookupRecord f val')) | (f, fty) <- canonicalFields fields ] > pure $ VRecord [ (f, go fty (lookupRecord f val'))
> | (f, fty) <- canonicalFields fields ]
> TVFun _ bty -> pure $ VFun (\v -> go bty (appFun val v)) > TVFun _ bty -> pure $ VFun (\v -> go bty (appFun val v))
> TVInteger -> evalPanic "logicUnary" ["Integer not in class Logic"] > TVInteger -> evalPanic "logicUnary" ["Integer not in class Logic"]
> TVIntMod _ -> evalPanic "logicUnary" ["Z not in class Logic"] > TVIntMod _ -> evalPanic "logicUnary" ["Z not in class Logic"]
@ -953,16 +982,27 @@ at the same positions.
> go :: TValue -> E Value -> E Value -> E Value > go :: TValue -> E Value -> E Value -> E Value
> go ty l r = > go ty l r =
> case ty of > case ty of
> TVBit -> VBit <$> (op <$> (fromVBit <$> l) <*> (fromVBit <$> r)) > TVBit ->
> TVSeq w ety -> VList (Nat w) <$> (zipWith (go ety) <$> (fromVList <$> l) <*> (fromVList <$> r)) > VBit <$> (op <$> (fromVBit <$> l) <*> (fromVBit <$> r))
> TVStream ety -> VList Inf <$> (zipWith (go ety) <$> (fromVList <$> l) <*> (fromVList <$> r)) > TVSeq w ety ->
> TVTuple etys -> VTuple <$> (zipWith3 go etys <$> (fromVTuple <$> l) <*> (fromVTuple <$> r)) > VList (Nat w) <$> (zipWith (go ety) <$>
> TVRec fields -> do l' <- l > (fromVList <$> l) <*>
> r' <- r > (fromVList <$> r))
> pure $ VRecord > TVStream ety ->
> [ (f, go fty (lookupRecord f l') (lookupRecord f r')) > VList Inf <$> (zipWith (go ety) <$>
> | (f, fty) <- canonicalFields fields > (fromVList <$> l) <*>
> ] > (fromVList <$> r))
> TVTuple etys ->
> VTuple <$> (zipWith3 go etys <$>
> (fromVTuple <$> l) <*>
> (fromVTuple <$> r))
> TVRec fields ->
> do l' <- l
> r' <- r
> pure $ VRecord
> [ (f, go fty (lookupRecord f l') (lookupRecord f r'))
> | (f, fty) <- canonicalFields fields
> ]
> TVFun _ bty -> pure $ VFun $ \v -> > TVFun _ bty -> pure $ VFun $ \v ->
> do l' <- l > do l' <- l
> r' <- r > r' <- r
@ -1054,7 +1094,8 @@ False]`, but to `error "foo"`.
> VTuple . zipWith go tys . fromVTuple <$> val > VTuple . zipWith go tys . fromVTuple <$> val
> TVRec fs -> > TVRec fs ->
> do val' <- val > do val' <- val
> pure $ VRecord [ (f, go fty (lookupRecord f val')) | (f, fty) <- canonicalFields fs ] > pure $ VRecord [ (f, go fty (lookupRecord f val'))
> | (f, fty) <- canonicalFields fs ]
> TVAbstract {} -> > TVAbstract {} ->
> evalPanic "arithUnary" ["Abstract type not in `Ring`"] > evalPanic "arithUnary" ["Abstract type not in `Ring`"]
@ -1077,22 +1118,32 @@ False]`, but to `error "foo"`.
> TVRational -> > TVRational ->
> VRational <$> appOp2 qop (fromVRational <$> l) (fromVRational <$> r) > VRational <$> appOp2 qop (fromVRational <$> l) (fromVRational <$> r)
> TVFloat e p -> > TVFloat e p ->
> VFloat . fpToBF e p <$> appOp2 (flop e p) (fromVFloat <$> l) (fromVFloat <$> r) > VFloat . fpToBF e p <$>
> appOp2 (flop e p) (fromVFloat <$> l) (fromVFloat <$> r)
> TVArray{} -> > TVArray{} ->
> evalPanic "arithBinary" ["Array not in class Ring"] > evalPanic "arithBinary" ["Array not in class Ring"]
> TVSeq w a > TVSeq w a
> | isTBit a -> vWord w <$> appOp2 iop (fromVWord =<< l) (fromVWord =<< r) > | isTBit a -> vWord w <$> appOp2 iop (fromVWord =<< l) (fromVWord =<< r)
> | otherwise -> VList (Nat w) <$> (zipWith (go a) <$> (fromVList <$> l) <*> (fromVList <$> r)) > | otherwise ->
> VList (Nat w) <$> (zipWith (go a) <$>
> (fromVList <$> l) <*>
> (fromVList <$> r))
> TVStream a -> > TVStream a ->
> VList Inf <$> (zipWith (go a) <$> (fromVList <$> l) <*> (fromVList <$> r)) > VList Inf <$> (zipWith (go a) <$>
> (fromVList <$> l) <*>
> (fromVList <$> r))
> TVFun _ ety -> > TVFun _ ety ->
> pure $ VFun (\x -> go ety (appFun l x) (appFun r x)) > pure $ VFun (\x -> go ety (appFun l x) (appFun r x))
> TVTuple tys -> > TVTuple tys ->
> VTuple <$> (zipWith3 go tys <$> (fromVTuple <$> l) <*> (fromVTuple <$> r)) > VTuple <$> (zipWith3 go tys <$>
> (fromVTuple <$> l) <*>
> (fromVTuple <$> r))
> TVRec fs -> > TVRec fs ->
> do l' <- l > do l' <- l
> r' <- r > r' <- r
> pure $ VRecord [ (f, go fty (lookupRecord f l') (lookupRecord f r')) | (f, fty) <- canonicalFields fs ] > pure $ VRecord
> [ (f, go fty (lookupRecord f l') (lookupRecord f r'))
> | (f, fty) <- canonicalFields fs ]
> TVAbstract {} -> > TVAbstract {} ->
> evalPanic "arithBinary" ["Abstract type not in class `Ring`"] > evalPanic "arithBinary" ["Abstract type not in class `Ring`"]
@ -1163,9 +1214,10 @@ confused with integral division).
> (Integer -> Integer -> BigFloat -> BigFloat -> E BigFloat) -> > (Integer -> Integer -> BigFloat -> BigFloat -> E BigFloat) ->
> TValue -> E Value -> E Value -> E Value > TValue -> E Value -> E Value -> E Value
> fieldBinary qop flop ty l r = case ty of > fieldBinary qop flop ty l r = case ty of
> TVRational -> VRational <$> appOp2 qop (fromVRational <$> l) (fromVRational <$> r) > TVRational -> VRational <$>
> TVFloat e p -> VFloat . fpToBF e p <$> appOp2 (flop e p) > appOp2 qop (fromVRational <$> l) (fromVRational <$> r)
> (fromVFloat <$> l) (fromVFloat <$> r) > TVFloat e p -> VFloat . fpToBF e p <$>
> appOp2 (flop e p) (fromVFloat <$> l) (fromVFloat <$> r)
> _ -> evalPanic "fieldBinary" [show ty ++ " is not a Field type"] > _ -> evalPanic "fieldBinary" [show ty ++ " is not a Field type"]
> >
> ratDiv :: Rational -> Rational -> E Rational > ratDiv :: Rational -> Rational -> E Rational
@ -1242,13 +1294,15 @@ bits to the *left* of that position are equal.
> TVArray{} -> > TVArray{} ->
> evalPanic "lexCompare" ["invalid type"] > evalPanic "lexCompare" ["invalid type"]
> TVSeq _w ety -> > TVSeq _w ety ->
> lexList =<< (zipWith (lexCompare ety) <$> (fromVList <$> l) <*> (fromVList <$> r)) > lexList =<< (zipWith (lexCompare ety) <$>
> (fromVList <$> l) <*> (fromVList <$> r))
> TVStream _ -> > TVStream _ ->
> evalPanic "lexCompare" ["invalid type"] > evalPanic "lexCompare" ["invalid type"]
> TVFun _ _ -> > TVFun _ _ ->
> evalPanic "lexCompare" ["invalid type"] > evalPanic "lexCompare" ["invalid type"]
> TVTuple etys -> > TVTuple etys ->
> lexList =<< (zipWith3 lexCompare etys <$> (fromVTuple <$> l) <*> (fromVTuple <$> r)) > lexList =<< (zipWith3 lexCompare etys <$>
> (fromVTuple <$> l) <*> (fromVTuple <$> r))
> TVRec fields -> > TVRec fields ->
> do let tys = map snd (canonicalFields fields) > do let tys = map snd (canonicalFields fields)
> ls <- map snd . sortBy (comparing fst) . fromVRecord <$> l > ls <- map snd . sortBy (comparing fst) . fromVRecord <$> l
@ -1291,15 +1345,18 @@ fields are compared in alphabetical order.
> TVArray{} -> > TVArray{} ->
> evalPanic "lexSignedCompare" ["invalid type"] > evalPanic "lexSignedCompare" ["invalid type"]
> TVSeq _w ety > TVSeq _w ety
> | isTBit ety -> compare <$> (fromSignedVWord =<< l) <*> (fromSignedVWord =<< r) > | isTBit ety ->
> compare <$> (fromSignedVWord =<< l) <*> (fromSignedVWord =<< r)
> | otherwise -> > | otherwise ->
> lexList =<< (zipWith (lexSignedCompare ety) <$> (fromVList <$> l) <*> (fromVList <$> r)) > lexList =<< (zipWith (lexSignedCompare ety) <$>
> (fromVList <$> l) <*> (fromVList <$> r))
> TVStream _ -> > TVStream _ ->
> evalPanic "lexSignedCompare" ["invalid type"] > evalPanic "lexSignedCompare" ["invalid type"]
> TVFun _ _ -> > TVFun _ _ ->
> evalPanic "lexSignedCompare" ["invalid type"] > evalPanic "lexSignedCompare" ["invalid type"]
> TVTuple etys -> > TVTuple etys ->
> lexList =<< (zipWith3 lexSignedCompare etys <$> (fromVTuple <$> l) <*> (fromVTuple <$> r)) > lexList =<< (zipWith3 lexSignedCompare etys <$>
> (fromVTuple <$> l) <*> (fromVTuple <$> r))
> TVRec fields -> > TVRec fields ->
> do let tys = map snd (canonicalFields fields) > do let tys = map snd (canonicalFields fields)
> ls <- map snd . sortBy (comparing fst) . fromVRecord <$> l > ls <- map snd . sortBy (comparing fst) . fromVRecord <$> l
@ -1593,7 +1650,8 @@ Pretty Printing
> VFloat fl -> text (show (FP.fpPP opts fl)) > VFloat fl -> text (show (FP.fpPP opts fl))
> VList l vs -> > VList l vs ->
> case l of > case l of
> Inf -> ppList (map (ppEValue opts) (take (useInfLength opts) vs) ++ [text "..."]) > Inf -> ppList (map (ppEValue opts)
> (take (useInfLength opts) vs) ++ [text "..."])
> Nat n -> > Nat n ->
> -- For lists of defined bits, print the value as a numeral. > -- For lists of defined bits, print the value as a numeral.
> case traverse isBit vs of > case traverse isBit vs of