DEV Community

[Comment from a deleted post]
Collapse
 
shimmer profile image
Brian Berns • Edited

I see. Thanks for the detailed response.

Note that Vector itself still needs the rec keyword, so the static members can access the module level make function which is defined further down the file.

You might want to consider using type extensions to achieve this without the need for a forward reference:

let make x y z =
    {
        X = x
        Y = y
        Z = z
        W = 0.0
    }

type T with
    static member ( * ) (v, scalar) =
        make (v.X * scalar) (v.Y * scalar) (v.Z * scalar)

a nested type can easily be made abstract via a signature file

Ah, that's interesting, but would prevent access to the individual X, Y, Z, W values of the vector as well, right? That seems like it would be too limiting in your case.

Here are some additional thoughts on your implementation:

[Matrix] is also based on Array2D so the code ended up quite imperative in places

You're referring to for loops that generate sequences? IMHO, this approach is actually quite elegant and fully consistent with functional programming, so I would hope you keep it. However, for clarity, I would change this:

if abs (m1.[r, c] - m2.[r, c]) > epsilon then
    yield false

To this:

yield abs (m1.[r, c] - m2.[r, c]) <= epsilon

You can also simplify this:

seq {
   // code
}
|> Seq.toArray

To this:

[|
    // code
|]

I would also consider rewriting your final while loop to avoid the need for a mutable projectile. I suggest generating a sequence of immutable projectiles instead. Something like this:

let rec fly projectile =
    seq {
        if projectile.position.Y > 0.0 then
            yield projectile
            yield! projectile |> tick env |> fly
    }
for projectile in fly { position = ...; velocity = ... } do
    if onCanvas ... then
       Cavas.writePixel ...

I hope this is helpful. Happy New Year!