z0w0/helm

IsVec2 and vector class

shoooe opened this issue · 2 comments

The following is definitely low priority and possibly away from the author's intent for this library, especially considering that it doesn't mimic Elm. But I also think that this is an opportunity to make helm interface more "open world friendly", given that Haskell has a much wider public than Elm which is specific to its own web-restricted world.

Haskell's support for math with pairs is basic to say the least. This often leads the user of this library, to have to define a vector class of its own, with its own definition of Applicative, Functor and Num to make things a little better. An example is this:

data Vec2 a = Vec2 a a deriving (Eq, Show)

instance Functor Vec2 where
    fmap fn (Vec2 a b) = Vec2 (fn a) (fn b)

instance Applicative Vec2 where
    pure a = Vec2 a a
    (Vec2 fa fb) <*> (Vec2 a b) = Vec2 (fa a) (fb b)

instance (Num a) => Num (Vec2 a) where
    (+)           = liftA2 (+) 
    (*)           = liftA2 (*)
    abs           = fmap abs
    signum        = fmap signum
    fromInteger = pure . fromInteger
    negate        = fmap negate

magnitude :: Floating a => Vec2 a -> a
magnitude (Vec2 ax ay) = sqrt $ (ax ^^ (2 :: Int)) + (ay ^^ (2 :: Int))

normalize :: Floating a => Vec2 a -> Vec2 a
normalize v = liftA2 (/) v (pure . magnitude $ v)

The user will then need to define its own version of fromPair and toPair to later interact with helm.

One way to solve this would be to provide a Vec2 class for the library itself, but the user might need to use other vector data structures of their own (due to compatibility with other libraries; for example a physic library).

What if we define a simple class:

class IsVec2 vec where
    toPair     :: vec a -> (a, a)
    fromPair   :: (a, a) -> vec a

-- utility functions
getX :: IsVec2 => vec a -> a
getX = fst . toPair

getY :: vec a -> a
getY = snd . toPair

and then modify the interface so that it accepts and return any IsVec2 structure polymorphically? An example would be:

arrows' :: IsVec2 vec => Bool -> Bool -> Bool -> Bool -> vec Int
arrows' u l d r = fromPair $ (-1 * fromEnum l + 1 * fromEnum r, -1 * fromEnum u + 1 * fromEnum d)

or:

move :: IsVec2 vec => vec Double -> Form -> Form
move v f = f { formX = (getX v) + formX f, formY = (getY v) + formY f }

so the user could use move, and all the functions that currently accept or return pairs, directly with their own data structures:

move (Vec2 1 1) $ ...

In addition to this, we could provide a Math/Vector module that could contain a Vec2 data like above for the user to use directly if they have no other preferences.

z0w0 commented

There's two sides to what people would like Helm to continue on, some want it to copy Helm strictly and others want it only to mimic it slightly. I'm on both sides at the moment, leaning mainly towards the strict copy side.. so I'm going to say no to this sort of thing being part of the main library. But we could certainly make this sort of thing part of the extras library.

But we could certainly make this sort of thing part of the extras library.

Nah, no need to duplicate a library for this. Thanks for the attention though. :)