monad instance frustration
okay so as i have posted about a bunch before, i have this haskell
internally, it's defined like this:
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.
can take ages since it has to evaluate the spine of the entire 1000000000-long list,
(there's also
the problem is,
the problem, though, is that
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
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. :/
Enumerable
data type, that deals with decks of enumerable values. the fundamental interface is something likedata 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 likelength $ (,,) <$> [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. :/