{-# LANGUAGE BangPatterns #-}

-- | The process-wide numbering of types.
--
-- A data constructor closure carries a packed tag: the number of its type
-- in the high bits, the constructor's tag in the low ('Unison.Runtime.TypeTags').
-- The number stands for the type's 'Reference' wherever that is needed: to
-- order values of different types ('Unison.Runtime.Stack.compareClosure'),
-- to write a value out ('Unison.Runtime.Machine.reflectValue') or to show
-- it ('Unison.Runtime.Decompile'). The comparison runs in pure code
-- (@Ord Val@, the key order of a replaced @Map@) and has no cache in hand,
-- and a value built by one code cache can meet a value built by another
-- (ucm runs an ordinary and a sandboxed runtime), so the numbering is one
-- per process, here, and not a field of the cache.
--
-- Numbers are never reused and an entry never changes once written, which
-- is what makes the pure read ('typeReference') sound: a closure carrying
-- number @n@ exists only after @n@ was published, which is after its slot
-- was written. A number reaches another thread inside a closure or a
-- compiled group, through whatever synchronization handed those over.
--
-- Writes happen when code is loaded, rarely, and never block a reader:
-- the number-to-reference direction is a directory of fixed chunks that
-- are allocated on demand and never moved, read with two loads; the
-- reference-to-number direction is an immutable map swapped atomically.
--
-- The builtin types have fixed numbers ('builtinTypeNumbering'), the same
-- in every process; the compiled code of a program carries the numbering
-- it was compiled against, and the process that runs it seeds the registry
-- from that ('seedTypeNumbers') before loading anything.
module Unison.Runtime.TypeNumbers
  ( typeNumbers,
    typeNumber,
    typeReference,
    typeReferences,
    typeNumberMap,
    seedTypeNumbers,
  )
where

import Control.Exception (evaluate)
import Control.Monad.ST (RealWorld)
import Data.Bits (shiftR, (.&.))
import Data.IORef
import Data.Map.Strict qualified as Map
import Data.Primitive.Array
import Data.Primitive.SmallArray
import Data.Set qualified as Set
import GHC.IO (unsafeDupablePerformIO, unsafePerformIO)
import Unison.Prelude
import Unison.Reference (Reference)
import Unison.Runtime.Builtin.Types (builtinTypeNumbering)
import Unison.Runtime.InternalError (internalBug)
import Unison.Util.EnumContainers (EnumMap)
import Unison.Util.EnumContainers qualified as EC

-- | Both directions, swapped together so that a snapshot is consistent.
data Maps = Maps !(Map Reference Word64) !(EnumMap Word64 Reference)

data Registry = Registry
  { -- | the next number to hand out
    Registry -> IORef Word64
next :: !(IORef Word64),
    -- | number to reference, in chunks of 'chunkSize'; an entry is
    -- 'Nothing' until the first number in its chunk is assigned
    Registry
-> SmallArray (IORef (Maybe (MutableArray RealWorld Reference)))
directory :: !(SmallArray (IORef (Maybe (MutableArray RealWorld Reference)))),
    Registry -> IORef Maps
maps :: !(IORef Maps)
  }

chunkBits :: Int
chunkBits :: Int
chunkBits = Int
12

chunkSize :: Int
chunkSize :: Int
chunkSize = Int
4096

-- | 1024 chunks of 4096: four million types, far beyond what a process
-- could load.
directorySize :: Int
directorySize :: Int
directorySize = Int
1024

registry :: Registry
registry :: Registry
registry = IO Registry -> Registry
forall a. IO a -> a
unsafePerformIO do
  IORef Word64
next <- Word64 -> IO (IORef Word64)
forall a. a -> IO (IORef a)
newIORef (Word64
1 Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Map Reference Word64 -> Word64
forall a. Ord a => Map Reference a -> a
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum Map Reference Word64
builtinTypeNumbering)
  SmallArray (IORef (Maybe (MutableArray RealWorld Reference)))
directory <- Int
-> [IORef (Maybe (MutableArray RealWorld Reference))]
-> SmallArray (IORef (Maybe (MutableArray RealWorld Reference)))
forall a. Int -> [a] -> SmallArray a
smallArrayFromListN Int
directorySize ([IORef (Maybe (MutableArray RealWorld Reference))]
 -> SmallArray (IORef (Maybe (MutableArray RealWorld Reference))))
-> IO [IORef (Maybe (MutableArray RealWorld Reference))]
-> IO
     (SmallArray (IORef (Maybe (MutableArray RealWorld Reference))))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int
-> IO (IORef (Maybe (MutableArray RealWorld Reference)))
-> IO [IORef (Maybe (MutableArray RealWorld Reference))]
forall (m :: * -> *) a. Applicative m => Int -> m a -> m [a]
replicateM Int
directorySize (Maybe (MutableArray RealWorld Reference)
-> IO (IORef (Maybe (MutableArray RealWorld Reference)))
forall a. a -> IO (IORef a)
newIORef Maybe (MutableArray RealWorld Reference)
forall a. Maybe a
Nothing)
  IORef Maps
maps <- Maps -> IO (IORef Maps)
forall a. a -> IO (IORef a)
newIORef (Map Reference Word64 -> EnumMap Word64 Reference -> Maps
Maps Map Reference Word64
forall k a. Map k a
Map.empty EnumMap Word64 Reference
forall a. Monoid a => a
mempty)
  let reg :: Registry
reg = IORef Word64
-> SmallArray (IORef (Maybe (MutableArray RealWorld Reference)))
-> IORef Maps
-> Registry
Registry IORef Word64
next SmallArray (IORef (Maybe (MutableArray RealWorld Reference)))
directory IORef Maps
maps
  [(Reference, Word64)]
-> ((Reference, Word64) -> IO Word64) -> IO ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ (Map Reference Word64 -> [(Reference, Word64)]
forall k a. Map k a -> [(k, a)]
Map.toList Map Reference Word64
builtinTypeNumbering) \(Reference
r, Word64
n) -> Registry -> Word64 -> Reference -> IO Word64
assign Registry
reg Word64
n Reference
r
  pure Registry
reg
{-# NOINLINE registry #-}

-- | The chunk that holds number @n@, allocated if it was not.
chunkFor :: Registry -> Word64 -> IO (MutableArray RealWorld Reference)
chunkFor :: Registry -> Word64 -> IO (MutableArray RealWorld Reference)
chunkFor Registry
reg Word64
n
  | Int
ci Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
directorySize = [Word] -> [Char] -> IO (MutableArray RealWorld Reference)
forall a. HasCallStack => [Word] -> [Char] -> a
internalBug [] ([Char]
"TypeNumbers: number out of range: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Word64 -> [Char]
forall a. Show a => a -> [Char]
show Word64
n)
  | Bool
otherwise =
      IORef (Maybe (MutableArray RealWorld Reference))
-> IO (Maybe (MutableArray RealWorld Reference))
forall a. IORef a -> IO a
readIORef IORef (Maybe (MutableArray RealWorld Reference))
ref IO (Maybe (MutableArray RealWorld Reference))
-> (Maybe (MutableArray RealWorld Reference)
    -> IO (MutableArray RealWorld Reference))
-> IO (MutableArray RealWorld Reference)
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
        Just MutableArray RealWorld Reference
c -> MutableArray RealWorld Reference
-> IO (MutableArray RealWorld Reference)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure MutableArray RealWorld Reference
c
        Maybe (MutableArray RealWorld Reference)
Nothing -> do
          MutableArray RealWorld Reference
c <- Int -> Reference -> IO (MutableArray (PrimState IO) Reference)
forall (m :: * -> *) a.
PrimMonad m =>
Int -> a -> m (MutableArray (PrimState m) a)
newArray Int
chunkSize ([Word] -> [Char] -> Reference
forall a. HasCallStack => [Word] -> [Char] -> a
internalBug [] ([Char]
"TypeNumbers: unassigned type number near " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Word64 -> [Char]
forall a. Show a => a -> [Char]
show Word64
n))
          IORef (Maybe (MutableArray RealWorld Reference))
-> (Maybe (MutableArray RealWorld Reference)
    -> (Maybe (MutableArray RealWorld Reference),
        MutableArray RealWorld Reference))
-> IO (MutableArray RealWorld Reference)
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef (Maybe (MutableArray RealWorld Reference))
ref \case
            Just MutableArray RealWorld Reference
c' -> (MutableArray RealWorld Reference
-> Maybe (MutableArray RealWorld Reference)
forall a. a -> Maybe a
Just MutableArray RealWorld Reference
c', MutableArray RealWorld Reference
c')
            Maybe (MutableArray RealWorld Reference)
Nothing -> (MutableArray RealWorld Reference
-> Maybe (MutableArray RealWorld Reference)
forall a. a -> Maybe a
Just MutableArray RealWorld Reference
c, MutableArray RealWorld Reference
c)
  where
    ci :: Int
ci = Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word64
n Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftR` Int
chunkBits)
    ref :: IORef (Maybe (MutableArray RealWorld Reference))
ref = SmallArray (IORef (Maybe (MutableArray RealWorld Reference)))
-> Int -> IORef (Maybe (MutableArray RealWorld Reference))
forall a. SmallArray a -> Int -> a
indexSmallArray (Registry
-> SmallArray (IORef (Maybe (MutableArray RealWorld Reference)))
directory Registry
reg) Int
ci

-- | Writes slot @n@, then publishes the entry. Returns the number the
-- reference has afterwards: @n@, or the one another thread published
-- first, in which case slot @n@ stays a hole. Dies if @n@ was already
-- published for another reference, which only a seed can attempt.
assign :: Registry -> Word64 -> Reference -> IO Word64
assign :: Registry -> Word64 -> Reference -> IO Word64
assign Registry
reg Word64
n Reference
r = do
  MutableArray RealWorld Reference
c <- Registry -> Word64 -> IO (MutableArray RealWorld Reference)
chunkFor Registry
reg Word64
n
  MutableArray (PrimState IO) Reference -> Int -> Reference -> IO ()
forall (m :: * -> *) a.
PrimMonad m =>
MutableArray (PrimState m) a -> Int -> a -> m ()
writeArray MutableArray RealWorld Reference
MutableArray (PrimState IO) Reference
c (Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
n Int -> Int -> Int
forall a. Bits a => a -> a -> a
.&. (Int
chunkSize Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)) Reference
r
  Either Reference Word64
outcome <- IORef Maps
-> (Maps -> (Maps, Either Reference Word64))
-> IO (Either Reference Word64)
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' (Registry -> IORef Maps
maps Registry
reg) \ms :: Maps
ms@(Maps Map Reference Word64
fwd EnumMap Word64 Reference
bwd) -> case Reference -> Map Reference Word64 -> Maybe Word64
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Reference
r Map Reference Word64
fwd of
    Just Word64
n' -> (Maps
ms, Word64 -> Either Reference Word64
forall a b. b -> Either a b
Right Word64
n')
    Maybe Word64
Nothing -> case Word64 -> EnumMap Word64 Reference -> Maybe Reference
forall k a. EnumKey k => k -> EnumMap k a -> Maybe a
EC.lookup Word64
n EnumMap Word64 Reference
bwd of
      Just Reference
r' -> (Maps
ms, Reference -> Either Reference Word64
forall a b. a -> Either a b
Left Reference
r')
      Maybe Reference
Nothing -> (Map Reference Word64 -> EnumMap Word64 Reference -> Maps
Maps (Reference -> Word64 -> Map Reference Word64 -> Map Reference Word64
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert Reference
r Word64
n Map Reference Word64
fwd) (Word64
-> Reference
-> EnumMap Word64 Reference
-> EnumMap Word64 Reference
forall k a. EnumKey k => k -> a -> EnumMap k a -> EnumMap k a
EC.mapInsert Word64
n Reference
r EnumMap Word64 Reference
bwd), Word64 -> Either Reference Word64
forall a b. b -> Either a b
Right Word64
n)
  case Either Reference Word64
outcome of
    Right Word64
n' -> Word64 -> IO Word64
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Word64
n'
    Left Reference
r' -> [Word] -> [Char] -> IO Word64
forall a. HasCallStack => [Word] -> [Char] -> a
internalBug [] ([Char]
"TypeNumbers: number " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Word64 -> [Char]
forall a. Show a => a -> [Char]
show Word64
n [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
" is " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Reference -> [Char]
forall a. Show a => a -> [Char]
show Reference
r' [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
", cannot seed it as " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Reference -> [Char]
forall a. Show a => a -> [Char]
show Reference
r)

-- | Numbers the references that have none yet; the numbers of them all.
typeNumbers :: Set Reference -> IO (Map Reference Word64)
typeNumbers :: Set Reference -> IO (Map Reference Word64)
typeNumbers Set Reference
rs = do
  Maps Map Reference Word64
fwd EnumMap Word64 Reference
_ <- IORef Maps -> IO Maps
forall a. IORef a -> IO a
readIORef (Registry -> IORef Maps
maps Registry
registry)
  let new :: Set Reference
new = (Reference -> Bool) -> Set Reference -> Set Reference
forall a. (a -> Bool) -> Set a -> Set a
Set.filter (Reference -> Map Reference Word64 -> Bool
forall k a. Ord k => k -> Map k a -> Bool
`Map.notMember` Map Reference Word64
fwd) Set Reference
rs
  [(Reference, Word64)]
assigned <- [Reference]
-> (Reference -> IO (Reference, Word64))
-> IO [(Reference, Word64)]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
t a -> (a -> f b) -> f (t b)
for (Set Reference -> [Reference]
forall a. Set a -> [a]
Set.toList Set Reference
new) \Reference
r -> do
    Word64
n <- IORef Word64 -> (Word64 -> (Word64, Word64)) -> IO Word64
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' (Registry -> IORef Word64
next Registry
registry) \Word64
i -> (Word64
i Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Word64
1, Word64
i)
    (Reference
r,) (Word64 -> (Reference, Word64))
-> IO Word64 -> IO (Reference, Word64)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Registry -> Word64 -> Reference -> IO Word64
assign Registry
registry Word64
n Reference
r
  pure ([(Reference, Word64)] -> Map Reference Word64
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [(Reference, Word64)]
assigned Map Reference Word64
-> Map Reference Word64 -> Map Reference Word64
forall a. Semigroup a => a -> a -> a
<> Map Reference Word64 -> Set Reference -> Map Reference Word64
forall k a. Ord k => Map k a -> Set k -> Map k a
Map.restrictKeys Map Reference Word64
fwd Set Reference
rs)

-- | The number of one reference, assigned if it had none.
typeNumber :: Reference -> IO Word64
typeNumber :: Reference -> IO Word64
typeNumber Reference
r = do
  Maps Map Reference Word64
fwd EnumMap Word64 Reference
_ <- IORef Maps -> IO Maps
forall a. IORef a -> IO a
readIORef (Registry -> IORef Maps
maps Registry
registry)
  case Reference -> Map Reference Word64 -> Maybe Word64
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Reference
r Map Reference Word64
fwd of
    Just Word64
n -> Word64 -> IO Word64
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Word64
n
    Maybe Word64
Nothing -> (Map Reference Word64 -> Reference -> Word64
forall k a. Ord k => Map k a -> k -> a
Map.! Reference
r) (Map Reference Word64 -> Word64)
-> IO (Map Reference Word64) -> IO Word64
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Set Reference -> IO (Map Reference Word64)
typeNumbers (Reference -> Set Reference
forall a. a -> Set a
Set.singleton Reference
r)

-- | The reference a number stands for. Pure: an entry never changes once
-- written, and a number is only ever read out of something built after
-- the entry was published (see the module comment). A number never
-- assigned is an internal error.
typeReference :: Word64 -> Reference
typeReference :: Word64 -> Reference
typeReference Word64
n = IO Reference -> Reference
forall a. IO a -> a
unsafeDupablePerformIO do
  IORef (Maybe (MutableArray RealWorld Reference))
-> IO (Maybe (MutableArray RealWorld Reference))
forall a. IORef a -> IO a
readIORef (SmallArray (IORef (Maybe (MutableArray RealWorld Reference)))
-> Int -> IORef (Maybe (MutableArray RealWorld Reference))
forall a. SmallArray a -> Int -> a
indexSmallArray (Registry
-> SmallArray (IORef (Maybe (MutableArray RealWorld Reference)))
directory Registry
registry) (Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word64
n Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftR` Int
chunkBits))) IO (Maybe (MutableArray RealWorld Reference))
-> (Maybe (MutableArray RealWorld Reference) -> IO Reference)
-> IO Reference
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Just MutableArray RealWorld Reference
c -> MutableArray (PrimState IO) Reference -> Int -> IO Reference
forall (m :: * -> *) a.
PrimMonad m =>
MutableArray (PrimState m) a -> Int -> m a
readArray MutableArray RealWorld Reference
MutableArray (PrimState IO) Reference
c (Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
n Int -> Int -> Int
forall a. Bits a => a -> a -> a
.&. (Int
chunkSize Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)) IO Reference -> (Reference -> IO Reference) -> IO Reference
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Reference -> IO Reference
forall a. a -> IO a
evaluate
    Maybe (MutableArray RealWorld Reference)
Nothing -> [Word] -> [Char] -> IO Reference
forall a. HasCallStack => [Word] -> [Char] -> a
internalBug [] ([Char]
"TypeNumbers: unassigned type number " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Word64 -> [Char]
forall a. Show a => a -> [Char]
show Word64
n)
{-# NOINLINE typeReference #-}

-- | Every number assigned so far, with its reference.
typeReferences :: IO (EnumMap Word64 Reference)
typeReferences :: IO (EnumMap Word64 Reference)
typeReferences = (\(Maps Map Reference Word64
_ EnumMap Word64 Reference
bwd) -> EnumMap Word64 Reference
bwd) (Maps -> EnumMap Word64 Reference)
-> IO Maps -> IO (EnumMap Word64 Reference)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IORef Maps -> IO Maps
forall a. IORef a -> IO a
readIORef (Registry -> IORef Maps
maps Registry
registry)

-- | Every reference numbered so far, with its number.
typeNumberMap :: IO (Map Reference Word64)
typeNumberMap :: IO (Map Reference Word64)
typeNumberMap = (\(Maps Map Reference Word64
fwd EnumMap Word64 Reference
_) -> Map Reference Word64
fwd) (Maps -> Map Reference Word64)
-> IO Maps -> IO (Map Reference Word64)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IORef Maps -> IO Maps
forall a. IORef a -> IO a
readIORef (Registry -> IORef Maps
maps Registry
registry)

-- | Takes on a numbering made elsewhere: a compiled program's. Each entry
-- must agree with what this process has, if it has it; the counter moves
-- past the largest number.
seedTypeNumbers :: EnumMap Word64 Reference -> IO ()
seedTypeNumbers :: EnumMap Word64 Reference -> IO ()
seedTypeNumbers EnumMap Word64 Reference
m = do
  [(Word64, Reference)] -> ((Word64, Reference) -> IO ()) -> IO ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ (EnumMap Word64 Reference -> [(Word64, Reference)]
forall k a. EnumKey k => EnumMap k a -> [(k, a)]
EC.mapToList EnumMap Word64 Reference
m) \(Word64
n, Reference
_) ->
    IORef Word64 -> (Word64 -> (Word64, ())) -> IO ()
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' (Registry -> IORef Word64
next Registry
registry) \Word64
i -> (Word64 -> Word64 -> Word64
forall a. Ord a => a -> a -> a
max Word64
i (Word64
n Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Word64
1), ())
  [(Word64, Reference)] -> ((Word64, Reference) -> IO ()) -> IO ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ (EnumMap Word64 Reference -> [(Word64, Reference)]
forall k a. EnumKey k => EnumMap k a -> [(k, a)]
EC.mapToList EnumMap Word64 Reference
m) \(Word64
n, Reference
r) -> do
    Word64
n' <- Registry -> Word64 -> Reference -> IO Word64
assign Registry
registry Word64
n Reference
r
    Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Word64
n' Word64 -> Word64 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word64
n) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
      [Word] -> [Char] -> IO ()
forall a. HasCallStack => [Word] -> [Char] -> a
internalBug [] ([Char]
"TypeNumbers: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Reference -> [Char]
forall a. Show a => a -> [Char]
show Reference
r [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
" is number " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Word64 -> [Char]
forall a. Show a => a -> [Char]
show Word64
n' [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
" here, cannot seed it as " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Word64 -> [Char]
forall a. Show a => a -> [Char]
show Word64
n)