Reputation: 125
I am going through the Learning F# Through Game Development book and in chapter 3 I am getting a TypeInitializationException with the following code:
// edit: missing definitions, thanks John
let earth_mass = 5.97e24<kg> // defined in other file that is ref'd in ok
let moon_mass = 7.35e22<kg>
let lerp (x:float<'T>) (y:float<'T>) (a:float) = (x * a) + (y * (1.0 - a))
// inside list comprehension for loop -
let m = (lerp earth_mass moon_mass (rand.NextDouble())) * 1.0e-4 // error!!
I've downloaded the authors source to see if I missed something but it yields the same error. The exception message gives little clue to what the issue is either (to me at least).
Here is the whole code for this chapter for reference:
namespace Games
module Chapter3 =
open System
open System.Threading
open Games.Math
type Asteroid =
{
Position : Vector2<m>
Velocity : Vector2<m/s>
Mass : float<kg>
Name : string
}
let dt = 60.0<s>
let G = 6.67e-11<m^3 * kg^-1 * s^-2>
let earth_radius = 6.37e6<m>
let field_size = earth_radius * 60.0
let max_velocity = 2.3e4<m/s>
let earth_mass = 5.97e24<kg>
let moon_mass = 7.35e22<kg>
let create_field num_asteroids =
let lerp (x:float<'T>) (y:float<'T>) (a:float) = x * a + y * (1.0 - a)
let rand = Random()
[
for i = 1 to num_asteroids do
let m = (lerp earth_mass moon_mass (rand.NextDouble())) * 1.0e-4
let x = lerp 0.0<m> field_size (rand.NextDouble())
let y = lerp 0.0<m> field_size (rand.NextDouble())
let vx = max_velocity * (rand.NextDouble() * 2.0 - 1.0) * 0.1
let vy = max_velocity * (rand.NextDouble() * 2.0 - 1.0) * 0.1
yield
{
Position = { X = x; Y = y}
Velocity = { X = vx; Y = vy}
Mass = m
Name = "a"
}
]
let f0 = create_field 20
let clamp (p:Vector2<_>, v:Vector2<_>) =
let p,v =
if p.X < 0.0<_> then
{p with X = 0.0<_>}, {v with X = -v.X}
else
p,v
let p,v =
if p.X > field_size then
{p with X = field_size}, {v with X = -v.X}
else
p,v
let p,v =
if p.Y < 0.0<_> then
{p with Y = 0.0<_>}, {v with Y = -v.Y}
else
p,v
let p,v =
if p.Y > field_size then
{p with Y = field_size}, {v with Y = -v.Y}
else
p,v
p,v
let force (a:Asteroid, a':Asteroid) =
let dir = a'.Position - a.Position
let dist = dir.Length + 1.0<m>
G * a.Mass * a'.Mass * dir/(dist * dist * dist)
let simulation_step (asteroids:Asteroid list) =
[
for a in asteroids do
let forces =
[
for a' in asteroids do
if a' <> a then
yield force(a, a')
]
let F = List.sum forces
let p', v' = clamp(a.Position, a.Velocity)
yield
{
a with
Position = p' + dt * v'
Velocity = v' + dt * F/a.Mass
}
]
let print_scene (asteroids:Asteroid list) =
do Console.Clear()
for i = 0 to 79 do
Console.SetCursorPosition(i, 0)
Console.Write("*")
Console.SetCursorPosition(i, 23)
Console.Write("*")
for j = 0 to 23 do
Console.SetCursorPosition(0, j)
Console.Write("*")
Console.SetCursorPosition(79, j)
Console.Write("*")
let set_cursor_on_body b =
Console.SetCursorPosition(
((b.Position.X/4.0e8<m>) * 78.0 + 1.0) |> int,
((b.Position.Y/4.0e8<m>) * 23.0 + 1.0) |> int)
for a in asteroids do
do set_cursor_on_body a
do Console.Write(a.Name)
do Thread.Sleep(100)
let simulation() =
let rec simulation m =
do print_scene m
let m' = simulation_step m
do simulation m'
do simulation f0
Code for Math module:
namespace Games
module Math =
[<Measure>]
type m //metres
[<Measure>]
type kg //kilogram
[<Measure>]
type s // seconds
[<Measure>]
type N = kg*m/s^2 //Newtons
type Vector2<[<Measure>]'T> =
{
X : float<'T>
Y : float<'T>
}
static member Zero : Vector2<'T> =
{ X = 0.0<_>; Y = 0.0<_> }
static member (+) (v1:Vector2<'T>, v2:Vector2<'T>) : Vector2<'T> =
{ X = v1.X + v2.X; Y = v1.Y + v2.Y }
static member (+) (v:Vector2<'T>, k:float<'T>) : Vector2<'T> =
{ X = v.X + k; Y = v.Y + k }
static member (+) (k:float<'T>, v:Vector2<'T>) : Vector2<'T> = v + k
static member (~-) (v:Vector2<'T>) : Vector2<'T> =
{ X = -v.X; Y = -v.Y }
static member (-) (v1:Vector2<'T>, v2:Vector2<'T>) : Vector2<'T> =
v1 + (-v2)
static member (-) (v:Vector2<'T>, k:float<'T>) : Vector2<'T> =
v + (-k)
static member (-) (k:float<'T>, v:Vector2<'T>) : Vector2<'T> =
k + (-v)
static member (*) (v1:Vector2<'a>, v2:Vector2<'b>) : Vector2<'a * 'b> =
{ X = v1.X * v2.X; Y = v1.Y * v2.Y }
static member (*) (v:Vector2<'a>, f:float<'b>) : Vector2<'a * 'b> =
{ X = v.X * f; Y = v.Y * f }
static member (*) (f:float<'b>, v:Vector2<'a>) : Vector2<'b * 'a> =
{ X = f * v.X; Y = f * v.Y }
static member (/) (v:Vector2<'a>, f:float<'b>) : Vector2<'a / 'b> =
v * (1.0/f)
member this.Length : float<'a> =
sqrt((this.X * this.X + this.Y * this.Y))
static member Distance(v1:Vector2<'T>, v2:Vector2<'T>) =
(v1 - v2).Length
static member Normalize(v:Vector2<'T>) : Vector2<1> =
v / v.Length
Code to run example:
[<EntryPoint>]
let main argv =
Games.Chapter3.simulation()
0 // return an integer exit code
Could someone please explain what is going on, using VS2013 and F# 3.1/.Net4.5
thank you.
Mick
Upvotes: 2
Views: 291
Reputation: 16792
It believe you are running into this F# compiler bug. It's present in F# 3.1.2 and earlier, but has since been fixed, so in 4.0 and later this will work.
Per the issue discussion, in F# 3.0 and earlier the bug existed, but didn't always cause a runtime crash. Looking at the book's sample code project, I'm guessing the author wrote these samples using F# 3.0 or earlier, and did not realize they were triggering the bug.
Workarounds:
Upvotes: 1