-
Notifications
You must be signed in to change notification settings - Fork 18
/
Common.fsx
129 lines (101 loc) · 3.87 KB
/
Common.fsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
(*
Common.fsx
Part of "Thirteen ways of looking at a turtle"
Related blog post: http://fsharpforfunandprofit.com/posts/13-ways-of-looking-at-a-turtle/
*)
open System
// ======================================
// Common types and helper functions
// ======================================
/// An alias for a float
type Distance = float
/// Use a unit of measure to make it clear that the angle is in degrees, not radians
type [<Measure>] Degrees
/// An alias for a float of Degrees
type Angle = float<Degrees>
/// Enumeration of available pen states
type PenState = Up | Down
/// Enumeration of available pen colors
type PenColor = Black | Red | Blue
/// A structure to store the (x,y) coordinates
type Position = {x:float; y:float}
// ======================================
// Common helper functions
// ======================================
// round a float to two places to make it easier to read
let round2 (flt:float) = Math.Round(flt,2)
/// calculate a new position from the current position given an angle and a distance
let calcNewPosition (distance:Distance) (angle:Angle) currentPos =
// Convert degrees to radians with 180.0 degrees = 1 pi radian
let angleInRads = angle * (Math.PI/180.0) * 1.0<1/Degrees>
// current pos
let x0 = currentPos.x
let y0 = currentPos.y
// new pos
let x1 = x0 + (distance * cos angleInRads)
let y1 = y0 + (distance * sin angleInRads)
// return a new Position
{x=round2 x1; y=round2 y1}
/// Default initial state
let initialPosition,initialColor,initialPenState =
{x=0.0; y=0.0}, Black, Down
/// Emulating a real implementation for drawing a line
let dummyDrawLine log oldPos newPos color =
// for now just log it
log (sprintf "...Draw line from (%0.1f,%0.1f) to (%0.1f,%0.1f) using %A" oldPos.x oldPos.y newPos.x newPos.y color)
/// trim a string
let trimString (str:string) = str.Trim()
// ======================================
// Result companion module
// ======================================
module Result =
let returnR x =
Ok x
// infix version of bind
let ( >>= ) xR f =
Result.bind f xR
// infix version of map
let ( <!> ) = Result.map
let applyR fR xR =
fR >>= (fun f ->
xR >>= (fun x ->
returnR (f x) ))
// infix version of apply
let ( <*> ) = applyR
// lift a one-parameter function to result world (same as mapR)
let lift1R f x = f <!> x
// lift a two-parameter function to result world
let lift2R f x y = f <!> x <*> y
/// Computation Expression
type ResultBuilder() =
member this.Bind(m:Result<'a,'error>,f:'a -> Result<'b,'error>) =
Result.bind f m
member this.Return(x) :Result<'a,'error> =
returnR x
member this.ReturnFrom(m) :Result<'a,'error> =
m
member this.Zero() :Result<unit,'error> =
this.Return ()
member this.Combine(m1, f) =
this.Bind(m1, f)
member this.Delay(f) =
f
member this.Run(m) =
m()
member this.TryWith(m:Result<'a,'error>, h: exn -> Result<'a,'error>) =
try this.ReturnFrom(m)
with e -> h e
member this.TryFinally(m:Result<'a,'error>, compensation) =
try this.ReturnFrom(m)
finally compensation()
member this.Using(res:#IDisposable, body) : Result<'b,'error> =
this.TryFinally(body res, (fun () -> match res with null -> () | disp -> disp.Dispose()))
member this.While(cond, m) =
if not (cond()) then
this.Zero()
else
this.Bind(m(), fun _ -> this.While(cond, m))
member this.For(sequence:seq<_>, body) =
this.Using(sequence.GetEnumerator(),
(fun enum -> this.While(enum.MoveNext, fun _ -> body enum.Current)))
let result = ResultBuilder()