let it leave me like a long breath

let it dissipate or fade in the background

"we believe in rough consensus and running code"

Profile

xax: purple-orange {11/3 knotwork star, pointed down (Default)
howling howling howling

Nav

  • Recent Entries
  • Archive
  • Reading
  • Tags
  • Memories
  • Profile

Tags

  • art - 2 uses
  • asteroid garden - 4 uses
  • code - 19 uses
  • demos - 1 use
  • dreams - 5 uses
  • ff7 fangame - 23 uses
  • fic prompts - 13 uses
  • gamedev challenge - 82 uses
  • hell game - 76 uses
  • nanowrimo - 11 uses
  • plants - 9 uses
  • process - 52 uses
  • programming - 51 uses
  • screenshots - 5 uses
  • writing log - 83 uses

May 2025

S M T W T F S
    123
45678 910
1112131415 1617
18192021222324
25262728293031
  • Oct. 21st, 2015
  • xax: purple-orange {11/3 knotwork star, pointed down (Default)
    [personal profile] xax
    • Current Music: Groove Armada - Paper Romance (Softwar Remix)
    Tags:
    • code,
    • programming
    posted @ 08:34 pm

    "we believe in rough consensus and running code"

    here's some running code i wrote, which i share to inspire you all:

    {-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies, TypeOperators, KindSignatures #-}
    
    module Generation.Layer
      ( Generator(load, check)
      , (?)(..)
      , base
      , layer
      , split
      , render0
      , render1
      , render2
      , render3
      , render4
      , render5
      -- etc
      -- for writing type signatures only:
      , Base
      , Layer
      , Split
      , Term ()
      , Extend ()
      ) where
    
    import Prelude hiding (lookup, map)
    
    import Control.Arrow
    import Control.Applicative
    
    import Data.Maybe (fromMaybe)
    import Data.Map hiding (foldr, split)
    import Data.Monoid
    import qualified Data.Set
    
    {-
    things to add to make this more practically useful / useful at all
    
     * unify the extractX and renderX functions, dear god
       * can typefamilies help with this?? can anything help this.
    
     * unload functions. either explicit (unload x chunk) or implicit (unload all chunks not needed to keep ... (or the area within X distance of it) fully loaded) but i guess it comes down to the same thing really.
    
     * it would be a radical rearrangement, but a threaded IO version that you ask for coordinates and get a ticket back (a STM ref that either holds 'unloaded' or 'loaded x' or smth), & meanwhile you can poll a stream (??) to see which parts are currently being loaded or unloaded. right now it's very... blocking, and i could imagine generation of something a dozen layers up causing huge hitches as it forceloads huge chunks of stuff
    -}
    
    -- at first glance that fundep might seem too exclusive, except all the instances actually look like `Generator (Thing ... m k v) m k v`, which, is basically about as explicit a dependency as you can get
    class Generator g m k v | g -> m k v where
      load :: g -> k -> m g
      check :: g -> k -> Maybe v
      -- unloadSaveFor :: g -> [k] -> Int -> g
    
    base :: Ord k => (k -> m v) -> Base m k v
    base rv = Base rv mempty
    
    layer :: (Ord k, Generator a m k u) => ([(k, u)] -> k -> u -> m v) -> Int -> (k -> [k]) -> a -> Layer a u m k v
    layer gen spread adj prev = Layer gen adj spread mempty prev
    
    split :: (Ord k, Generator a m j v) => (j -> k -> v -> v) -> (j -> [k]) -> (k -> j) -> a -> Split a j m k v
    split distribute split merge prev = Split distribute split merge mempty prev
    
    instance (Ord k, Monad m) => Generator (Base m k v) m k v where
      load g k = case check g k of
        Nothing -> do
          v <- _gen g k
          return $ g {_bstore = insert k v (_bstore g)}
        Just _ -> return g
      check g k = lookup k (_bstore g)
    
    instance (Ord k, Monad m, Generator a m k u) => Generator (Layer a u m k v) m k v where
      load l k = case check l k of
        Nothing -> do
          let adjs = adjacentSpread (_adjacent l) (_spread l) k
          (p', adjvs) <- mapAccumM forceLoad (_lprev l) (k:adjs)
          w <- _layer l (zip adjs $ tail adjvs) k (head adjvs)
          return $ l {_lprev = p', _lstore = insert k w (_lstore l)}
        Just _ -> return l
      check l k = lookup k (_lstore l)
    
    instance (Ord k, Ord j, Monad m, Generator a m j v) => Generator (Split a j m k v) m k v where
      load s k = case check s k of
        Nothing -> do
          (p', m) <- forceLoad (_sprev s) (_merge s k)
          let kvs = redistribute (_split s) (_distribute s) (_merge s k) m
          let s' = foldr (uncurry insert) (_sstore s) kvs
          return $ s {_sprev = p', _sstore = s'}
        Just _ -> return s
      check s k = lookup k (_sstore s)
    
    data Base m k v = Base
      { _gen :: k -> m v
      , _bstore :: Map k v
      }
    
    instance Functor m => Functor (Base m k) where
      fmap f b = b {_gen = fmap f . _gen b, _bstore = map f $ _bstore b}
    
    data Layer a u m k v = Layer
      { _layer :: [(k, u)] -> k -> u -> m v
      , _adjacent :: k -> [k]
      -- how many steps adjacent does this layer require data from?
      , _spread :: Int
      , _lstore :: Map k v
      , _lprev :: a
      }
    
    -- for rekeying a generator, and specifically for rekeying a generator where each 'chunk' is split into smaller chunks
    data Split a j (m :: * -> *) k v = Split
      { _distribute :: j -> k -> v -> v
      , _split :: j -> [k]
      -- inverse of _split, kind of: any & all `k`s generated from a call to split should generate the `j` that would generate them
      , _merge :: k -> j
      , _sstore :: Map k v
      , _sprev :: a
      }
    
    forceLoad :: (Generator g m k v, Monad m) => g -> k -> m (g, v)
    forceLoad g k = do
      g' <- load g k
      return (g', fromMaybe (error "bad forceLoad") $ check g' k)
    
    -- i think this works
    adjacentSpread :: Ord k => (k -> [k]) -> Int -> k -> [k]
    adjacentSpread adj spread k =
      iterate (\as -> nub . (as ++) . (adj =<<) $ as) [k] !! spread
    
    redistribute :: (j -> [k]) -> (j -> k -> v -> v) -> j -> v -> [(k, v)]
    redistribute split distribute j v = fmap (\k -> (k, distribute j k v)) $ split j
    
    mapAccumM :: Monad m => (acc -> x -> m (acc, y)) -> acc -> [x] -> m (acc, [y])
    mapAccumM _ z [] = return (z, [])
    mapAccumM f z (x:xs) = do
      (z', y) <- f z x
      (zf, ys) <- mapAccumM f z' xs
      return (zf, y:ys)
    
    nub :: Ord k => [k] -> [k]
    nub = Data.Set.toList . Data.Set.fromList
    
    {---
    - RENDERING FUNCTION(S)
    -}
    
    infixr 5 ?
    data a ? b = a :? Maybe b
    
    render0
      :: ( Ord k, Term t k v
         )
      => t -> (v -> r) -> Map k r
    render0 t f = map f . extract0 $ t
    
    render1
      :: ( Ord k, Term t k v
         , Ord k', Extend e t k k' v'
         )
      => e -> (v ? v' -> r) -> Map k' r
    render1 t f = map f . extract1 $ t
    
    render2
      :: ( Ord k, Term t k v
         , Ord k', Extend e t k k' v'
         , Ord k'', Extend f e k' k'' v''
         )
      => f -> (v ? v' ? v'' -> r) -> Map k'' r
    render2 t f = map f . extract2 $ t
    
    render3
      :: ( Ord k, Term t k v
         , Ord k', Extend e t k k' v'
         , Ord k'', Extend f e k' k'' v''
         , Ord k''', Extend g f k'' k''' v'''
         )
      => g -> (v ? v' ? v'' ? v''' -> r) -> Map k''' r
    render3 t f = map f . extract3 $ t
    
    render4
      :: ( Ord k, Term t k v
         , Ord k', Extend e t k k' v'
         , Ord k'', Extend f e k' k'' v''
         , Ord k''', Extend g f k'' k''' v'''
         , Ord k'''', Extend h g k''' k'''' v''''
         )
      => h -> (v ? v' ? v'' ? v''' ? v'''' -> r) -> Map k'''' r
    render4 t f = map f . extract4 $ t
    
    render5
      :: ( Ord k, Term t k v
         , Ord k', Extend e t k k' v'
         , Ord k'', Extend f e k' k'' v''
         , Ord k''', Extend g f k'' k''' v'''
         , Ord k'''', Extend h g k''' k'''' v''''
         , Ord k''''', Extend i h k'''' k''''' v'''''
         )
      => i -> (v ? v' ? v'' ? v''' ? v'''' ? v''''' -> r) -> Map k''''' r
    render5 t f = map f . extract5 $ t
    
    extract0
      :: (Ord k, Term t k v
         )
      => t -> Map k v
    extract0 t = final t
    
    extract1
      :: ( Ord k, Term t k v
         , Ord k', Extend e t k k' v'
         )
      => e -> Map k' (v ? v')
    extract1 e = case next e of
      (t, v') -> case final t of
        v -> unify v' $ rekey e v
    
    extract2
      :: ( Ord k, Term t k v
         , Ord k', Extend e t k k' v'
         , Ord k'', Extend f e k' k'' v''
         )
      => f -> Map k'' (v ? v' ? v'')
    extract2 f = case next f of
      (e, v'') -> case next e of
        (t, v') -> case final t of
          v ->
            unify
              (unify
                v''
                $ rekey f v')
              $ rekey f . rekey e $ v
    
    extract3
      :: ( Ord k, Term t k v
         , Ord k', Extend e t k k' v'
         , Ord k'', Extend f e k' k'' v''
         , Ord k''', Extend g f k'' k''' v'''
         )
      => g -> Map k''' (v ? v' ? v'' ? v''')
    extract3 g = case next g of
      (f, v''') -> case next f of
        (e, v'') -> case next e of
          (t, v') -> case final t of
            v ->
              unify
                (unify
                  (unify
                    v'''
                    $ rekey g v'')
                  $ rekey g . rekey f $ v')
                $ rekey g . rekey f . rekey e $ v
    
    extract4
      :: ( Ord k, Term t k v
         , Ord k', Extend e t k k' v'
         , Ord k'', Extend f e k' k'' v''
         , Ord k''', Extend g f k'' k''' v'''
         , Ord k'''', Extend h g k''' k'''' v''''
         )
      => h -> Map k'''' (v ? v' ? v'' ? v''' ? v'''')
    extract4 h = case next h of
      (g, v'''') -> case next g of
        (f, v''') -> case next f of
          (e, v'') -> case next e of
            (t, v') -> case final t of
              v ->
                unify
                  (unify
                    (unify
                      (unify
                        v''''
                        $ rekey h v''')
                      $ rekey h . rekey g $ v'')
                    $ rekey h . rekey g . rekey f $ v')
                  $ rekey h . rekey g . rekey f . rekey e $ v
    
    extract5
      :: ( Ord k, Term t k v
         , Ord k', Extend e t k k' v'
         , Ord k'', Extend f e k' k'' v''
         , Ord k''', Extend g f k'' k''' v'''
         , Ord k'''', Extend h g k''' k'''' v''''
         , Ord k''''', Extend i h k'''' k''''' v'''''
         )
      => i -> Map k''''' (v ? v' ? v'' ? v''' ? v'''' ? v''''')
    extract5 i = case next i of
      (h, v''''') -> case next h of
        (g, v'''') -> case next g of
          (f, v''') -> case next f of
            (e, v'') -> case next e of
              (t, v') -> case final t of
                v ->
                  unify
                    (unify
                      (unify
                        (unify
                          (unify
                            v'''''
                            $ rekey i v'''')
                          $ rekey i . rekey h $ v''')
                        $ rekey i . rekey h . rekey g $ v'')
                      $ rekey i . rekey h . rekey g . rekey f $ v')
                    $ rekey i . rekey h . rekey g . rekey f . rekey e $ v
    
    class Term t k v | t -> k v where
      final :: t -> Map k v
    
    class Extend e t j k v | e -> t j k v where
      next :: e -> (t, Map k v)
      -- note the unbound type variable here. this means we can't use Split's distribute, so there's going to be some imprecision when it comes time to actually render. this means that rendering functions should do some bounds checking when it would be relevant, or else there might be some serious overdraw if like a x27-divided key has all its components rerendered
      rekey :: e -> Map j a -> Map k a
    
    instance Term (Base m k v) k v where
      final = _bstore
    
    instance Extend (Layer a u m k v) a k k v where
      next = _lprev &&& _lstore
      rekey _ = id
    
    spread :: Ord b => (a -> [b]) -> Map a v -> Map b v
    spread f m = fromList $ (\(k, v) -> (,) <$> f k <*> pure v ) =<< toList m
    
    instance Ord k => Extend (Split a j m k v) a j k v where
      next = _sprev &&& _sstore
      rekey s = spread (_split s)
    
    {-
    from data.map:
    "When calling `mergeWithKey combine only1 only2`, a function combining two IntMaps is created, such that
     * if a key is present in both maps, it is passed with both corresponding values to the combine function. Depending on the result, the key is either present in the result with specified value, or is left out;
     * a nonempty subtree present only in the first map is passed to only1 and the output is added to the result;
     * a nonempty subtree present only in the second map is passed to only2 and the output is added to the result."
    -}
    unify :: Ord k => Map k a -> Map k b -> Map k (b ? a)
    unify = mergeWithKey
      (\_ a b -> Just (b :? Just a))
      -- has a loaded 'higher' layer without a loaded 'lower' layer. this could
      -- just be `const empty`, if we want it to quietly fail instead of crash.
      -- but right now i'd like to know if this case ever happens, since it's
      -- real bad. i could probably be including the key, too.
      (error "unify: bad generation")
      (map $ \b -> b :? Nothing)
    



    • Previous Entry
    • Add Memory
    • Share This Entry
    • Next Entry
    • Reply
Page generated Jan. 24th, 2026 07:53 am
Powered by Dreamwidth Studios

Style Credit

  • Style: (No Theme) for vertical