let it leave me like a long breath

let it dissipate or fade in the background

monad instance frustration

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
  • Nov. 3rd, 2017
  • xax: purple-orange {11/3 knotwork star, pointed down (Default)
    [personal profile] xax
    Tags:
    • code,
    • programming
    posted @ 03:48 pm

    monad instance frustration

    okay so as i have posted about a bunch before, i have this haskell Enumerable data type, that deals with decks of enumerable values. the fundamental interface is something like

    data Enumerable a = ...
    
    instance Functor Enumerable ...
    instance Applicative Enumerable ...
    instance Alternative Enumerable ...
    instance Monoid Enumerable ...
    
    count :: Enumerable a -> Integer
    select :: Enumerable a -> Integer -> Maybe a
    enumerate :: Enumerable a -> [a]
    
    from :: [a] -> Enumerable a
    range :: Enum a => (a, a) -> Enumerable a

    internally, it's defined like this:

    data Enumerable a
    	= EnumerableNil
    	| Enumerable
    		{ count :: Integer
    		, selector :: Integer -> a
    		}

    and all the instances are function composition / application, so this is a reasonably-efficient way to store and generate values from a deck of values, even when that deck can be ludicrously huge. enumerate can be a dangerous function to use, given that these decks can have billions or trillions of entries, and that's simply because trying to generate billions-to-trillions of values is always a challenge. up until that point, there's no internal code that needs to exhaustively enumerate all values. so where something like
    length $ (,,) <$> [1..1000] <*> [1..1000] <*> [1..1000]
    can take ages since it has to evaluate the spine of the entire 1000000000-long list,
    count $ (,,) <$> from [1..1000] <*> from [1..1000] <*> from [1..1000] evaluates basically instantly, since all it needs to do is evaluate the three individual 1000-length lists. this is, in short, a way to get O(n) performance from combinatorial operations that would usually take O(2n) (or O(n2)?? i am not 100% on my big-os).

    (there's also roll :: RandomGen g => Enumerable a -> Rand g a, for randomly pulling from the deck, and is a fairly simple combination of count and select w/ generating a random number)

    the problem is, Enumerable should also be a monad. or rather, it is absolutely a monad, and writing a join :: Enumerable (Enumerable a) -> Enumerable a function is actually extremely trivial:

    join :: Enumerable (Enumerable a) -> Enumerable a
    join EnumerableNil = EnumerableNil
    join (Enumerable c v) = mconcat $ (\c' -> v c') <$> [0..c-1]

    the problem, though, is that [0..c-1] bit, which requires exhaustively evaluating every value in the deck, and thus completely wrecks the point of the data structure. so like, yeah, it's easy to write a Monad instance, here it is right here:

    instance Monad Enumerable where
    	return = pure
    	EnumerableNil >>= f = EnumerableNil
    	e >>= f = joinEnum $ f <$> e
    
    instance MonadPlus Enumerable where
    	mzero = EnumerableNil
    	mplus = (<|>)

    but that instance has god-awful performance on any non-trivial value, and given the way the value works (we need to know the number of combinations so we can select properly, and with a nested value there's no way to figure out how many combinations the inner Enumerable has without evaluating it) i don't think there's any feasible way to implement a Monad instance.

    this is super annoying, since the npc generator i'm working on now is INCREDIBLY simple if there's a monad instance for enumerable, and much much more complex if there's not. but. i don't think it's feasible to write any monad instance whatsoever. :/

    • Previous Entry
    • Add Memory
    • Share This Entry
    • Next Entry
    • Reply
Page generated Jul. 20th, 2025 02:57 am
Powered by Dreamwidth Studios

Style Credit

  • Style: (No Theme) for vertical