├── .gitattributes
├── .gitignore
├── .ionide
└── symbolCache.db
├── .vscode
└── extensions.json
├── Completed
├── FSharpWorkshopCompleted.sln
├── Module1
│ ├── Application
│ │ ├── Application.fsproj
│ │ ├── Functions.fs
│ │ ├── Try.fsx
│ │ └── Types.fs
│ ├── Demo.fsx
│ └── Tests
│ │ ├── Tests.fs
│ │ └── Tests.fsproj
├── Module2
│ ├── Application
│ │ ├── Application.fsproj
│ │ ├── Functions.fs
│ │ ├── Try.fsx
│ │ └── Types.fs
│ ├── Demo.fsx
│ └── Tests
│ │ ├── Tests.fs
│ │ └── Tests.fsproj
├── Module3
│ ├── Application
│ │ ├── Application.fsproj
│ │ ├── Functions.fs
│ │ ├── Try.fsx
│ │ └── Types.fs
│ ├── Demo.fsx
│ └── Tests
│ │ ├── Tests.fs
│ │ └── Tests.fsproj
└── Module4
│ ├── Application
│ ├── Application.fsproj
│ ├── Data.json
│ ├── Functions.fs
│ ├── Program.fs
│ ├── Services.fs
│ ├── Try.fsx
│ └── Types.fs
│ ├── Demo.fsx
│ └── Tests
│ ├── Tests.fs
│ └── Tests.fsproj
├── FSharpWorkshop.sln
├── FSharpWorkshop_Exercises.docx
├── FSharpWorkshop_Exercises.pdf
├── FSharpWorkshop_Slides.pdf
├── FSharpWorkshop_Slides.pptx
├── LICENSE
├── Module1
├── Application
│ ├── Application.fsproj
│ ├── Functions.fs
│ ├── Try.fsx
│ └── Types.fs
├── Demo.fsx
└── Tests
│ ├── Tests.fs
│ └── Tests.fsproj
├── Module2
├── Application
│ ├── Application.fsproj
│ ├── Functions.fs
│ ├── Try.fsx
│ └── Types.fs
├── Demo.fsx
└── Tests
│ ├── Tests.fs
│ └── Tests.fsproj
├── Module3
├── Application
│ ├── Application.fsproj
│ ├── Functions.fs
│ ├── Try.fsx
│ └── Types.fs
├── Demo.fsx
└── Tests
│ ├── Tests.fs
│ └── Tests.fsproj
├── Module4
├── Application
│ ├── Application.fsproj
│ ├── Data.json
│ ├── Functions.fs
│ ├── Program.fs
│ ├── Services.fs
│ ├── Try.fsx
│ ├── Types.fs
│ └── paket.references
├── Demo.fsx
└── Tests
│ ├── Tests.fs
│ └── Tests.fsproj
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 |
9 | # Build results
10 | [Dd]ebug/
11 | [Dd]ebugPublic/
12 | [Rr]elease/
13 | [Rr]eleases/
14 | x64/
15 | x86/
16 | build/
17 | bld/
18 | [Bb]in/
19 | [Oo]bj/
20 |
21 | # Roslyn cache directories
22 | *.ide/
23 |
24 | # MSTest test Results
25 | [Tt]est[Rr]esult*/
26 | [Bb]uild[Ll]og.*
27 |
28 | #NUNIT
29 | *.VisualState.xml
30 | TestResult.xml
31 |
32 | # Build Results of an ATL Project
33 | [Dd]ebugPS/
34 | [Rr]eleasePS/
35 | dlldata.c
36 |
37 | *_i.c
38 | *_p.c
39 | *_i.h
40 | *.ilk
41 | *.meta
42 | *.obj
43 | *.pch
44 | *.pdb
45 | *.pgc
46 | *.pgd
47 | *.rsp
48 | *.sbr
49 | *.tlb
50 | *.tli
51 | *.tlh
52 | *.tmp
53 | *.tmp_proj
54 | *.log
55 | *.vspscc
56 | *.vssscc
57 | .builds
58 | *.pidb
59 | *.svclog
60 | *.scc
61 |
62 | # Chutzpah Test files
63 | _Chutzpah*
64 |
65 | # Visual C++ cache files
66 | ipch/
67 | *.aps
68 | *.ncb
69 | *.opensdf
70 | *.sdf
71 | *.cachefile
72 |
73 | # Visual Studio profiler
74 | *.psess
75 | *.vsp
76 | *.vspx
77 |
78 | # TFS 2012 Local Workspace
79 | $tf/
80 |
81 | # Guidance Automation Toolkit
82 | *.gpState
83 |
84 | # ReSharper is a .NET coding add-in
85 | _ReSharper*/
86 | *.[Rr]e[Ss]harper
87 | *.DotSettings.user
88 |
89 | # JustCode is a .NET coding addin-in
90 | .JustCode
91 |
92 | # TeamCity is a build add-in
93 | _TeamCity*
94 |
95 | # DotCover is a Code Coverage Tool
96 | *.dotCover
97 |
98 | # NCrunch
99 | _NCrunch_*
100 | .*crunch*.local.xml
101 |
102 | # MightyMoose
103 | *.mm.*
104 | AutoTest.Net/
105 |
106 | # Web workbench (sass)
107 | .sass-cache/
108 |
109 | # Installshield output folder
110 | [Ee]xpress/
111 |
112 | # DocProject is a documentation generator add-in
113 | DocProject/buildhelp/
114 | DocProject/Help/*.HxT
115 | DocProject/Help/*.HxC
116 | DocProject/Help/*.hhc
117 | DocProject/Help/*.hhk
118 | DocProject/Help/*.hhp
119 | DocProject/Help/Html2
120 | DocProject/Help/html
121 |
122 | # Click-Once directory
123 | publish/
124 |
125 | # Publish Web Output
126 | *.[Pp]ublish.xml
127 | *.azurePubxml
128 | # TODO: Comment the next line if you want to checkin your web deploy settings
129 | # but database connection strings (with potential passwords) will be unencrypted
130 | *.pubxml
131 | *.publishproj
132 |
133 | # NuGet Packages
134 | *.nupkg
135 | # The packages folder can be ignored because of Package Restore
136 | **/packages/*
137 | # except build/, which is used as an MSBuild target.
138 | !**/packages/build/
139 | # If using the old MSBuild-Integrated Package Restore, uncomment this:
140 | #!**/packages/repositories.config
141 |
142 | # Windows Azure Build Output
143 | csx/
144 | *.build.csdef
145 |
146 | # Windows Store app package directory
147 | AppPackages/
148 |
149 | # Others
150 | sql/
151 | *.Cache
152 | ClientBin/
153 | [Ss]tyle[Cc]op.*
154 | ~$*
155 | *~
156 | *.dbmdl
157 | *.dbproj.schemaview
158 | *.pfx
159 | *.publishsettings
160 | node_modules/
161 |
162 | # RIA/Silverlight projects
163 | Generated_Code/
164 |
165 | # Backup & report files from converting an old project file
166 | # to a newer Visual Studio version. Backup files are not needed,
167 | # because we have git ;-)
168 | _UpgradeReport_Files/
169 | Backup*/
170 | UpgradeLog*.XML
171 | UpgradeLog*.htm
172 |
173 | # SQL Server files
174 | *.mdf
175 | *.ldf
176 |
177 | # Business Intelligence projects
178 | *.rdl.data
179 | *.bim.layout
180 | *.bim_*.settings
181 |
182 | # Microsoft Fakes
183 | FakesAssemblies/
184 |
185 | # ===================================================
186 | # Exclude F# project specific directories and files
187 | # ===================================================
188 |
189 | # NuGet Packages Directory
190 | packages/
191 |
192 | # Generated documentation folder
193 | docs/output/
194 |
195 | # Temp folder used for publishing docs
196 | temp/
197 |
198 | # Test results produced by build
199 | TestResults.xml
200 |
201 | # Nuget outputs
202 | nuget/*.nupkg
203 | release.cmd
204 | release.sh
205 | localpackages/
206 | paket-files
207 | *.orig
208 | .paket/paket.exe
209 | docs/content/license.md
210 | docs/content/release-notes.md
211 | .fake
212 | docs/tools/FSharp.Formatting.svclog
213 | testsOutput/
214 | appOutput/
215 | .vs/
--------------------------------------------------------------------------------
/.ionide/symbolCache.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jorgef/fsharpworkshop/f06b9e0089bb2d13e953b30fdcd1a1a01adf1698/.ionide/symbolCache.db
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "ionide.ionide-fsharp"
4 | ]
5 | }
--------------------------------------------------------------------------------
/Completed/FSharpWorkshopCompleted.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26124.0
5 | MinimumVisualStudioVersion = 15.0.26124.0
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Module1", "Module1", "{E022C6AD-65E1-4C44-A463-02C4A74B8EF1}"
7 | EndProject
8 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Application", "Module1\Application\Application.fsproj", "{6A431D66-F545-441A-82CD-5B85ADD0F7C0}"
9 | EndProject
10 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Tests", "Module1\Tests\Tests.fsproj", "{DDA9C6FC-F823-4C19-8424-E2AA84BFFFF6}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Module2", "Module2", "{DD79A08C-010B-4EC8-895F-2AFEB2965E98}"
13 | EndProject
14 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Application", "Module2\Application\Application.fsproj", "{BE23CC93-43AC-4A50-926F-3786F5B4E70A}"
15 | EndProject
16 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Tests", "Module2\Tests\Tests.fsproj", "{FE0D694D-C926-47F1-B803-24D950F081AF}"
17 | EndProject
18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Module3", "Module3", "{D7B10CEF-35E3-4555-9FD0-9D6D398A2E06}"
19 | EndProject
20 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Application", "Module3\Application\Application.fsproj", "{369DFD0D-5358-4440-8F27-49FD9FC288A7}"
21 | EndProject
22 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Tests", "Module3\Tests\Tests.fsproj", "{B6ED19F4-06B9-446A-A2D1-6064AA5AC641}"
23 | EndProject
24 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Module4", "Module4", "{90309E30-02DF-4E18-A43D-55438D7A521D}"
25 | EndProject
26 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Application", "Module4\Application\Application.fsproj", "{69D1888C-EAF2-4DF0-BCD1-0BB9A481E568}"
27 | EndProject
28 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Tests", "Module4\Tests\Tests.fsproj", "{2C2EB367-DAE0-4DB0-9CA6-048D332DEDFB}"
29 | EndProject
30 | Global
31 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
32 | Debug|Any CPU = Debug|Any CPU
33 | Debug|x64 = Debug|x64
34 | Debug|x86 = Debug|x86
35 | Release|Any CPU = Release|Any CPU
36 | Release|x64 = Release|x64
37 | Release|x86 = Release|x86
38 | EndGlobalSection
39 | GlobalSection(SolutionProperties) = preSolution
40 | HideSolutionNode = FALSE
41 | EndGlobalSection
42 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
43 | {6A431D66-F545-441A-82CD-5B85ADD0F7C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
44 | {6A431D66-F545-441A-82CD-5B85ADD0F7C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
45 | {6A431D66-F545-441A-82CD-5B85ADD0F7C0}.Debug|x64.ActiveCfg = Debug|Any CPU
46 | {6A431D66-F545-441A-82CD-5B85ADD0F7C0}.Debug|x64.Build.0 = Debug|Any CPU
47 | {6A431D66-F545-441A-82CD-5B85ADD0F7C0}.Debug|x86.ActiveCfg = Debug|Any CPU
48 | {6A431D66-F545-441A-82CD-5B85ADD0F7C0}.Debug|x86.Build.0 = Debug|Any CPU
49 | {6A431D66-F545-441A-82CD-5B85ADD0F7C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
50 | {6A431D66-F545-441A-82CD-5B85ADD0F7C0}.Release|Any CPU.Build.0 = Release|Any CPU
51 | {6A431D66-F545-441A-82CD-5B85ADD0F7C0}.Release|x64.ActiveCfg = Release|Any CPU
52 | {6A431D66-F545-441A-82CD-5B85ADD0F7C0}.Release|x64.Build.0 = Release|Any CPU
53 | {6A431D66-F545-441A-82CD-5B85ADD0F7C0}.Release|x86.ActiveCfg = Release|Any CPU
54 | {6A431D66-F545-441A-82CD-5B85ADD0F7C0}.Release|x86.Build.0 = Release|Any CPU
55 | {DDA9C6FC-F823-4C19-8424-E2AA84BFFFF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
56 | {DDA9C6FC-F823-4C19-8424-E2AA84BFFFF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
57 | {DDA9C6FC-F823-4C19-8424-E2AA84BFFFF6}.Debug|x64.ActiveCfg = Debug|Any CPU
58 | {DDA9C6FC-F823-4C19-8424-E2AA84BFFFF6}.Debug|x64.Build.0 = Debug|Any CPU
59 | {DDA9C6FC-F823-4C19-8424-E2AA84BFFFF6}.Debug|x86.ActiveCfg = Debug|Any CPU
60 | {DDA9C6FC-F823-4C19-8424-E2AA84BFFFF6}.Debug|x86.Build.0 = Debug|Any CPU
61 | {DDA9C6FC-F823-4C19-8424-E2AA84BFFFF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
62 | {DDA9C6FC-F823-4C19-8424-E2AA84BFFFF6}.Release|Any CPU.Build.0 = Release|Any CPU
63 | {DDA9C6FC-F823-4C19-8424-E2AA84BFFFF6}.Release|x64.ActiveCfg = Release|Any CPU
64 | {DDA9C6FC-F823-4C19-8424-E2AA84BFFFF6}.Release|x64.Build.0 = Release|Any CPU
65 | {DDA9C6FC-F823-4C19-8424-E2AA84BFFFF6}.Release|x86.ActiveCfg = Release|Any CPU
66 | {DDA9C6FC-F823-4C19-8424-E2AA84BFFFF6}.Release|x86.Build.0 = Release|Any CPU
67 | {BE23CC93-43AC-4A50-926F-3786F5B4E70A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
68 | {BE23CC93-43AC-4A50-926F-3786F5B4E70A}.Debug|Any CPU.Build.0 = Debug|Any CPU
69 | {BE23CC93-43AC-4A50-926F-3786F5B4E70A}.Debug|x64.ActiveCfg = Debug|Any CPU
70 | {BE23CC93-43AC-4A50-926F-3786F5B4E70A}.Debug|x64.Build.0 = Debug|Any CPU
71 | {BE23CC93-43AC-4A50-926F-3786F5B4E70A}.Debug|x86.ActiveCfg = Debug|Any CPU
72 | {BE23CC93-43AC-4A50-926F-3786F5B4E70A}.Debug|x86.Build.0 = Debug|Any CPU
73 | {BE23CC93-43AC-4A50-926F-3786F5B4E70A}.Release|Any CPU.ActiveCfg = Release|Any CPU
74 | {BE23CC93-43AC-4A50-926F-3786F5B4E70A}.Release|Any CPU.Build.0 = Release|Any CPU
75 | {BE23CC93-43AC-4A50-926F-3786F5B4E70A}.Release|x64.ActiveCfg = Release|Any CPU
76 | {BE23CC93-43AC-4A50-926F-3786F5B4E70A}.Release|x64.Build.0 = Release|Any CPU
77 | {BE23CC93-43AC-4A50-926F-3786F5B4E70A}.Release|x86.ActiveCfg = Release|Any CPU
78 | {BE23CC93-43AC-4A50-926F-3786F5B4E70A}.Release|x86.Build.0 = Release|Any CPU
79 | {FE0D694D-C926-47F1-B803-24D950F081AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
80 | {FE0D694D-C926-47F1-B803-24D950F081AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
81 | {FE0D694D-C926-47F1-B803-24D950F081AF}.Debug|x64.ActiveCfg = Debug|Any CPU
82 | {FE0D694D-C926-47F1-B803-24D950F081AF}.Debug|x64.Build.0 = Debug|Any CPU
83 | {FE0D694D-C926-47F1-B803-24D950F081AF}.Debug|x86.ActiveCfg = Debug|Any CPU
84 | {FE0D694D-C926-47F1-B803-24D950F081AF}.Debug|x86.Build.0 = Debug|Any CPU
85 | {FE0D694D-C926-47F1-B803-24D950F081AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
86 | {FE0D694D-C926-47F1-B803-24D950F081AF}.Release|Any CPU.Build.0 = Release|Any CPU
87 | {FE0D694D-C926-47F1-B803-24D950F081AF}.Release|x64.ActiveCfg = Release|Any CPU
88 | {FE0D694D-C926-47F1-B803-24D950F081AF}.Release|x64.Build.0 = Release|Any CPU
89 | {FE0D694D-C926-47F1-B803-24D950F081AF}.Release|x86.ActiveCfg = Release|Any CPU
90 | {FE0D694D-C926-47F1-B803-24D950F081AF}.Release|x86.Build.0 = Release|Any CPU
91 | {369DFD0D-5358-4440-8F27-49FD9FC288A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
92 | {369DFD0D-5358-4440-8F27-49FD9FC288A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
93 | {369DFD0D-5358-4440-8F27-49FD9FC288A7}.Debug|x64.ActiveCfg = Debug|Any CPU
94 | {369DFD0D-5358-4440-8F27-49FD9FC288A7}.Debug|x64.Build.0 = Debug|Any CPU
95 | {369DFD0D-5358-4440-8F27-49FD9FC288A7}.Debug|x86.ActiveCfg = Debug|Any CPU
96 | {369DFD0D-5358-4440-8F27-49FD9FC288A7}.Debug|x86.Build.0 = Debug|Any CPU
97 | {369DFD0D-5358-4440-8F27-49FD9FC288A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
98 | {369DFD0D-5358-4440-8F27-49FD9FC288A7}.Release|Any CPU.Build.0 = Release|Any CPU
99 | {369DFD0D-5358-4440-8F27-49FD9FC288A7}.Release|x64.ActiveCfg = Release|Any CPU
100 | {369DFD0D-5358-4440-8F27-49FD9FC288A7}.Release|x64.Build.0 = Release|Any CPU
101 | {369DFD0D-5358-4440-8F27-49FD9FC288A7}.Release|x86.ActiveCfg = Release|Any CPU
102 | {369DFD0D-5358-4440-8F27-49FD9FC288A7}.Release|x86.Build.0 = Release|Any CPU
103 | {B6ED19F4-06B9-446A-A2D1-6064AA5AC641}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
104 | {B6ED19F4-06B9-446A-A2D1-6064AA5AC641}.Debug|Any CPU.Build.0 = Debug|Any CPU
105 | {B6ED19F4-06B9-446A-A2D1-6064AA5AC641}.Debug|x64.ActiveCfg = Debug|Any CPU
106 | {B6ED19F4-06B9-446A-A2D1-6064AA5AC641}.Debug|x64.Build.0 = Debug|Any CPU
107 | {B6ED19F4-06B9-446A-A2D1-6064AA5AC641}.Debug|x86.ActiveCfg = Debug|Any CPU
108 | {B6ED19F4-06B9-446A-A2D1-6064AA5AC641}.Debug|x86.Build.0 = Debug|Any CPU
109 | {B6ED19F4-06B9-446A-A2D1-6064AA5AC641}.Release|Any CPU.ActiveCfg = Release|Any CPU
110 | {B6ED19F4-06B9-446A-A2D1-6064AA5AC641}.Release|Any CPU.Build.0 = Release|Any CPU
111 | {B6ED19F4-06B9-446A-A2D1-6064AA5AC641}.Release|x64.ActiveCfg = Release|Any CPU
112 | {B6ED19F4-06B9-446A-A2D1-6064AA5AC641}.Release|x64.Build.0 = Release|Any CPU
113 | {B6ED19F4-06B9-446A-A2D1-6064AA5AC641}.Release|x86.ActiveCfg = Release|Any CPU
114 | {B6ED19F4-06B9-446A-A2D1-6064AA5AC641}.Release|x86.Build.0 = Release|Any CPU
115 | {69D1888C-EAF2-4DF0-BCD1-0BB9A481E568}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
116 | {69D1888C-EAF2-4DF0-BCD1-0BB9A481E568}.Debug|Any CPU.Build.0 = Debug|Any CPU
117 | {69D1888C-EAF2-4DF0-BCD1-0BB9A481E568}.Debug|x64.ActiveCfg = Debug|Any CPU
118 | {69D1888C-EAF2-4DF0-BCD1-0BB9A481E568}.Debug|x64.Build.0 = Debug|Any CPU
119 | {69D1888C-EAF2-4DF0-BCD1-0BB9A481E568}.Debug|x86.ActiveCfg = Debug|Any CPU
120 | {69D1888C-EAF2-4DF0-BCD1-0BB9A481E568}.Debug|x86.Build.0 = Debug|Any CPU
121 | {69D1888C-EAF2-4DF0-BCD1-0BB9A481E568}.Release|Any CPU.ActiveCfg = Release|Any CPU
122 | {69D1888C-EAF2-4DF0-BCD1-0BB9A481E568}.Release|Any CPU.Build.0 = Release|Any CPU
123 | {69D1888C-EAF2-4DF0-BCD1-0BB9A481E568}.Release|x64.ActiveCfg = Release|Any CPU
124 | {69D1888C-EAF2-4DF0-BCD1-0BB9A481E568}.Release|x64.Build.0 = Release|Any CPU
125 | {69D1888C-EAF2-4DF0-BCD1-0BB9A481E568}.Release|x86.ActiveCfg = Release|Any CPU
126 | {69D1888C-EAF2-4DF0-BCD1-0BB9A481E568}.Release|x86.Build.0 = Release|Any CPU
127 | {2C2EB367-DAE0-4DB0-9CA6-048D332DEDFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
128 | {2C2EB367-DAE0-4DB0-9CA6-048D332DEDFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
129 | {2C2EB367-DAE0-4DB0-9CA6-048D332DEDFB}.Debug|x64.ActiveCfg = Debug|Any CPU
130 | {2C2EB367-DAE0-4DB0-9CA6-048D332DEDFB}.Debug|x64.Build.0 = Debug|Any CPU
131 | {2C2EB367-DAE0-4DB0-9CA6-048D332DEDFB}.Debug|x86.ActiveCfg = Debug|Any CPU
132 | {2C2EB367-DAE0-4DB0-9CA6-048D332DEDFB}.Debug|x86.Build.0 = Debug|Any CPU
133 | {2C2EB367-DAE0-4DB0-9CA6-048D332DEDFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
134 | {2C2EB367-DAE0-4DB0-9CA6-048D332DEDFB}.Release|Any CPU.Build.0 = Release|Any CPU
135 | {2C2EB367-DAE0-4DB0-9CA6-048D332DEDFB}.Release|x64.ActiveCfg = Release|Any CPU
136 | {2C2EB367-DAE0-4DB0-9CA6-048D332DEDFB}.Release|x64.Build.0 = Release|Any CPU
137 | {2C2EB367-DAE0-4DB0-9CA6-048D332DEDFB}.Release|x86.ActiveCfg = Release|Any CPU
138 | {2C2EB367-DAE0-4DB0-9CA6-048D332DEDFB}.Release|x86.Build.0 = Release|Any CPU
139 | EndGlobalSection
140 | GlobalSection(NestedProjects) = preSolution
141 | {6A431D66-F545-441A-82CD-5B85ADD0F7C0} = {E022C6AD-65E1-4C44-A463-02C4A74B8EF1}
142 | {DDA9C6FC-F823-4C19-8424-E2AA84BFFFF6} = {E022C6AD-65E1-4C44-A463-02C4A74B8EF1}
143 | {BE23CC93-43AC-4A50-926F-3786F5B4E70A} = {DD79A08C-010B-4EC8-895F-2AFEB2965E98}
144 | {FE0D694D-C926-47F1-B803-24D950F081AF} = {DD79A08C-010B-4EC8-895F-2AFEB2965E98}
145 | {369DFD0D-5358-4440-8F27-49FD9FC288A7} = {D7B10CEF-35E3-4555-9FD0-9D6D398A2E06}
146 | {B6ED19F4-06B9-446A-A2D1-6064AA5AC641} = {D7B10CEF-35E3-4555-9FD0-9D6D398A2E06}
147 | {69D1888C-EAF2-4DF0-BCD1-0BB9A481E568} = {90309E30-02DF-4E18-A43D-55438D7A521D}
148 | {2C2EB367-DAE0-4DB0-9CA6-048D332DEDFB} = {90309E30-02DF-4E18-A43D-55438D7A521D}
149 | EndGlobalSection
150 | EndGlobal
151 |
--------------------------------------------------------------------------------
/Completed/Module1/Application/Application.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Completed/Module1/Application/Functions.fs:
--------------------------------------------------------------------------------
1 | module Functions
2 |
3 | open Types
4 |
5 | let tryPromoteToVip purchases =
6 | let customer, amount = purchases
7 | if amount > 100M then { customer with IsVip = true }
8 | else customer
9 |
10 | let getPurchases customer =
11 | if customer.Id % 2 = 0 then (customer, 120M)
12 | else (customer, 80M)
--------------------------------------------------------------------------------
/Completed/Module1/Application/Try.fsx:
--------------------------------------------------------------------------------
1 | #load "Types.fs"
2 | #load "Functions.fs"
3 |
4 | open Types
5 | open Functions
6 |
7 | let customer = { Id =1; IsVip = false; Credit = 10M }
8 |
9 | let purchases = (customer, 101M)
10 | let vipCustomer = tryPromoteToVip purchases
11 |
12 | let calculatedPurchases = getPurchases customer
--------------------------------------------------------------------------------
/Completed/Module1/Application/Types.fs:
--------------------------------------------------------------------------------
1 | module Types
2 |
3 | type Customer = {
4 | Id: int
5 | IsVip: bool
6 | Credit: decimal
7 | }
--------------------------------------------------------------------------------
/Completed/Module1/Demo.fsx:
--------------------------------------------------------------------------------
1 | // Demo 1
2 |
3 | let a = 1
4 | let b = 2
5 | let sum x y = x + y
6 | let res = sum a b
7 |
8 | let myTuple = (42, "hello")
9 | let number, message = myTuple
10 |
11 | type MyRecord = { Number: int; Message: string }
12 | let myRecord = { Number = 42; Message = "hello" }
13 | let newRecord = { myRecord with Message = "hi" }
--------------------------------------------------------------------------------
/Completed/Module1/Tests/Tests.fs:
--------------------------------------------------------------------------------
1 | module Tests
2 |
3 | open Xunit
4 | open Swensen.Unquote
5 | open Types
6 | open Functions
7 |
8 | []
9 | let ``1-1 Create customer``() =
10 | let customer = { Id = 1; IsVip = false; Credit = 0M }
11 | test <@ customer.GetType () = typeof @>
12 |
13 | []
14 | let ``1-2 Promote to vip``() =
15 | let customer = { Id = 1; IsVip = false; Credit = 0M }
16 | let promotedCustomer = tryPromoteToVip (customer, 100.1M)
17 | test <@ promotedCustomer.IsVip = true @>
18 |
19 | []
20 | let ``1-3 Do not promote to vip``() =
21 | let customer = { Id = 1; IsVip = false; Credit = 0M }
22 | let promotedCustomer = tryPromoteToVip (customer, 99.9M)
23 | test <@ promotedCustomer.IsVip = false @>
24 |
25 | []
26 | let ``1-4 Get purchases for odd customers``() =
27 | let customer = { Id = 1; IsVip = false; Credit = 0M }
28 | let _, purchases = getPurchases customer
29 | test <@ purchases = 80M @>
30 |
31 | []
32 | let ``1-5 Get purchases for even customers``() =
33 | let customer = { Id = 2; IsVip = false; Credit = 0M }
34 | let _, purchases = getPurchases customer
35 | test <@ purchases = 120M @>
--------------------------------------------------------------------------------
/Completed/Module1/Tests/Tests.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Completed/Module2/Application/Application.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Completed/Module2/Application/Functions.fs:
--------------------------------------------------------------------------------
1 | module Functions
2 |
3 | open Types
4 |
5 | let tryPromoteToVip purchases =
6 | let customer, amount = purchases
7 | if amount > 100M then { customer with IsVip = true }
8 | else customer
9 |
10 | let getPurchases customer =
11 | if customer.Id % 2 = 0 then (customer, 120M)
12 | else (customer, 80M)
13 |
14 | let increaseCredit condition customer =
15 | if condition customer then { customer with Credit = customer.Credit + 100M }
16 | else { customer with Credit = customer.Credit + 50M }
17 |
18 | let increaseCreditUsingVip = increaseCredit (fun c -> c.IsVip)
19 |
20 | let upgradeCustomer = getPurchases >> tryPromoteToVip >> increaseCreditUsingVip
--------------------------------------------------------------------------------
/Completed/Module2/Application/Try.fsx:
--------------------------------------------------------------------------------
1 | #load "Types.fs"
2 | #load "Functions.fs"
3 |
4 | open Types
5 | open Functions
6 |
7 | let customer = { Id = 1; IsVip = false; Credit = 10M }
8 |
9 | let purchases = (customer, 101M)
10 | let vipCustomer = tryPromoteToVip purchases
11 |
12 | let calculatedPurchases = getPurchases customer
13 |
14 | let customerWithMoreCredit = increaseCredit (fun c -> c.IsVip) customer
15 |
16 | let upgradedCustomer = upgradeCustomer customer
--------------------------------------------------------------------------------
/Completed/Module2/Application/Types.fs:
--------------------------------------------------------------------------------
1 | module Types
2 |
3 | type Customer = {
4 | Id: int
5 | IsVip: bool
6 | Credit: decimal
7 | }
--------------------------------------------------------------------------------
/Completed/Module2/Demo.fsx:
--------------------------------------------------------------------------------
1 | // Demo 1
2 |
3 | let a = 1
4 | let b = 2
5 | let sum x y = x + y
6 | let res = sum a b
7 |
8 | let myTuple = (42, "hello")
9 | let number, message = myTuple
10 |
11 | type MyRecord = { Number: int; Message: string }
12 | let myRecord = { Number = 42; Message = "hello" }
13 | let newRecord = { myRecord with Message = "hi" }
14 |
15 |
16 | // Demo 2
17 |
18 | let compute (x: int) (y: int) (operation: int -> int -> int) = operation x y
19 | let res' = compute 1 2 sum
20 |
21 | let addOne = sum 1
22 | let addTwo = sum 2
23 |
24 | let res1 = addOne 1
25 | let res2 = addTwo res1
26 |
27 | let res2' =
28 | 1
29 | |> addOne
30 | |> addTwo
31 |
32 | let addThree = addOne >> addTwo
33 | let res2'' = addThree 1
--------------------------------------------------------------------------------
/Completed/Module2/Tests/Tests.fs:
--------------------------------------------------------------------------------
1 | module Tests
2 |
3 | open Xunit
4 | open Swensen.Unquote
5 | open Types
6 | open Functions
7 |
8 | []
9 | let ``2-1 Increase min credit using id``() =
10 | let customer = { Id = 1; IsVip = false; Credit = 0M }
11 | let upgradedCustomer = increaseCredit (fun c -> c.Id = 2) customer
12 | test <@ upgradedCustomer.Credit = 50M @>
13 |
14 | []
15 | let ``2-2 Increase max credit using id``() =
16 | let customer = { Id = 2; IsVip = false; Credit = 0M }
17 | let upgradedCustomer = increaseCredit (fun c -> c.Id = 2) customer
18 | test <@ upgradedCustomer.Credit = 100M @>
19 |
20 | []
21 | let ``2-3 Increase credit keeping existing one``() =
22 | let customer = { Id = 2; IsVip = false; Credit = 10M }
23 | let upgradedCustomer = increaseCredit (fun c -> c.Id = 2) customer
24 | test <@ upgradedCustomer.Credit = 110M @>
25 |
26 | []
27 | let ``2-4 Increase max credit using vip``() =
28 | let customer = { Id = 2; IsVip = true; Credit = 0M }
29 | let upgradedCustomer = increaseCreditUsingVip customer
30 | test <@ upgradedCustomer.Credit = 100M @>
31 |
32 | []
33 | let ``2-5 Upgrade customer with even id``() =
34 | let customer = { Id = 2; IsVip = false; Credit = 0M }
35 | let upgradedCustomer = upgradeCustomer customer
36 | test <@ upgradedCustomer.IsVip = true && upgradedCustomer.Credit = 100M @>
37 |
38 | []
39 | let ``2-6 Upgrade customer with odd id``() =
40 | let customer = { Id = 1; IsVip = false; Credit = 0M }
41 | let upgradedCustomer = upgradeCustomer customer
42 | test <@ upgradedCustomer.IsVip = false && upgradedCustomer.Credit = 50M @>
--------------------------------------------------------------------------------
/Completed/Module2/Tests/Tests.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Completed/Module3/Application/Application.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Completed/Module3/Application/Functions.fs:
--------------------------------------------------------------------------------
1 | module Functions
2 |
3 | open Types
4 | open System
5 |
6 | let tryPromoteToVip purchases =
7 | let customer, amount = purchases
8 | if amount > 100M then { customer with IsVip = true }
9 | else customer
10 |
11 | let getPurchases customer =
12 | if customer.Id % 2 = 0 then (customer, 120M)
13 | else (customer, 80M)
14 |
15 | let increaseCredit condition customer =
16 | if condition customer then { customer with Credit = customer.Credit + 100M }
17 | else { customer with Credit = customer.Credit + 50M }
18 |
19 | let increaseCreditUsingVip = increaseCredit (fun c -> c.IsVip)
20 |
21 | let upgradeCustomer = getPurchases >> tryPromoteToVip >> increaseCreditUsingVip
22 |
23 | let isAdult customer =
24 | match customer.PersonalDetails with
25 | | None -> false
26 | | Some d -> d.DateOfBirth.AddYears 18 <= DateTime.Now.Date
27 |
28 | let getAlert customer =
29 | match customer.Notifications with
30 | | ReceiveNotifications(receiveAlerts = true) ->
31 | sprintf "Alert for customer %i" customer.Id
32 | | _ -> ""
--------------------------------------------------------------------------------
/Completed/Module3/Application/Try.fsx:
--------------------------------------------------------------------------------
1 | #load "Types.fs"
2 | #load "Functions.fs"
3 |
4 | open System
5 | open Types
6 | open Functions
7 |
8 | let customer = {
9 | Id = 1
10 | IsVip = false
11 | Credit = 0M
12 | PersonalDetails = Some {
13 | FirstName = "John"
14 | LastName = "Doe"
15 | DateOfBirth = DateTime(1970, 11, 23)
16 | }
17 | Notifications = ReceiveNotifications(receiveDeals = true, receiveAlerts = true)
18 | }
19 |
20 | let purchases = (customer, 101M)
21 | let vipCustomer = tryPromoteToVip purchases
22 |
23 | let calculatedPurchases = getPurchases customer
24 |
25 | let customerWithMoreCredit = customer |> increaseCredit (fun c -> c.IsVip)
26 |
27 | let upgradedCustomer = upgradeCustomer customer
28 |
29 | let isAdultCustomer = isAdult customer
30 |
31 | let alertCustomer = getAlert customer
--------------------------------------------------------------------------------
/Completed/Module3/Application/Types.fs:
--------------------------------------------------------------------------------
1 | module Types
2 |
3 | open System
4 |
5 | type PersonalDetails = {
6 | FirstName: string
7 | LastName: string
8 | DateOfBirth: DateTime
9 | }
10 |
11 | [] type USD
12 | [] type EUR
13 |
14 | type Notifications =
15 | | NoNotifications
16 | | ReceiveNotifications of receiveDeals: bool * receiveAlerts: bool
17 |
18 | type Customer = {
19 | Id: int
20 | IsVip: bool
21 | Credit: decimal
22 | PersonalDetails: PersonalDetails option
23 | Notifications: Notifications
24 | }
--------------------------------------------------------------------------------
/Completed/Module3/Demo.fsx:
--------------------------------------------------------------------------------
1 | // Demo 1
2 |
3 | let a = 1
4 | let b = 2
5 | let sum x y = x + y
6 | let res = sum a b
7 |
8 | let myTuple = (42, "hello")
9 | let number, message = myTuple
10 |
11 | type MyRecord = { Number: int; Message: string }
12 | let myRecord = { Number = 42; Message = "hello" }
13 | let newRecord = { myRecord with Message = "hi" }
14 |
15 |
16 | // Demo 2
17 |
18 | let compute (x: int) (y: int) (operation: int -> int -> int) = operation x y
19 | let res' = compute 1 2 sum
20 |
21 | let addOne = sum 1
22 | let addTwo = sum 2
23 |
24 | let res1 = addOne 1
25 | let res2 = addTwo res1
26 |
27 | let res2' =
28 | 1
29 | |> addOne
30 | |> addTwo
31 |
32 | let addThree = addOne >> addTwo
33 | let res2'' = addThree 1
34 |
35 |
36 | // Demo 3
37 |
38 | let divide x y =
39 | match y with
40 | | 0 -> None
41 | | _ -> Some(x/y)
42 |
43 | let result = divide 4 2
44 | let result' = divide 4 0
45 |
46 | type DivisionResult =
47 | | DivisionSuccess of result: int
48 | | DivisionError of message: string
49 |
50 | let divide' x y =
51 | match y with
52 | |0 -> DivisionError(message = "Divide by zero")
53 | |_ -> DivisionSuccess(result = x / y)
54 |
55 | let result'' = divide' 4 2
56 | let result''' = divide' 4 0
57 |
58 | [] type m; [] type km; [] type h
59 | let distanceInMts = 11580.0
60 | let distanceInKms = 87.34
61 | //let totalDistance = distanceInMts + distanceInKms // Error
62 |
63 | let convertToKms (mts: float) =
64 | let m = mts / 1.0 // remove unit of measure
65 | let k = m / 1000.0 // convert
66 | k * 1.0 // add new unit of measure
67 |
68 | let convertToKms' (mts: float) = mts / 1000.0 * 1.0
69 |
70 | let convertedToKms = convertToKms distanceInMts
71 | let totalDistance' = convertedToKms + distanceInKms
72 | let speed = totalDistance' / 2.4
--------------------------------------------------------------------------------
/Completed/Module3/Tests/Tests.fs:
--------------------------------------------------------------------------------
1 | module Tests
2 |
3 | open System
4 | open Xunit
5 | open Swensen.Unquote
6 | open Types
7 | open Functions
8 |
9 | let customer = {
10 | Id = 1
11 | IsVip = false
12 | Credit = 0M
13 | PersonalDetails = Some {
14 | FirstName = "John"
15 | LastName = "Doe"
16 | DateOfBirth = DateTime(1970, 11, 23) }
17 | Notifications = ReceiveNotifications(receiveDeals = true,
18 | receiveAlerts = true)
19 | }
20 |
21 | []
22 | let ``3-1 Create customer``() =
23 | test <@ customer.GetType() = typeof @>
24 |
25 | []
26 | let ``3-2 Increase credit using USD``() =
27 | let upgradedCustomer = increaseCreditUsingVip customer
28 | test <@ upgradedCustomer.Credit = 50M @>
29 |
30 | []
31 | let ``3-3 Adult customer``() =
32 | test <@ customer |> isAdult @>
33 |
34 | []
35 | let ``3-4 Non-adult customer``() =
36 | let nonadult = { customer with PersonalDetails = Some { customer.PersonalDetails.Value with DateOfBirth = DateTime.Now.AddYears(-1) } }
37 | test <@ not (nonadult |> isAdult) @>
38 |
39 | []
40 | let ``3-5 Customer without personal details``() =
41 | let nonadult = { customer with PersonalDetails = None }
42 | test <@ not (nonadult |> isAdult) @>
43 |
44 | []
45 | let ``3-6 Get alert when nofications are enabled``() =
46 | let alert = customer |> getAlert
47 | test <@ alert = "Alert for customer 1" @>
48 |
49 | []
50 | let ``3-7 Do not get alert when nofications are disabled``() =
51 | let alert = { customer with Notifications = NoNotifications } |> getAlert
52 | test <@ alert = "" @>
--------------------------------------------------------------------------------
/Completed/Module3/Tests/Tests.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Completed/Module4/Application/Application.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 | Exe
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Always
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Completed/Module4/Application/Data.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "CustomerId": 1,
4 | "PurchasesByMonth": [ 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0 ]
5 | },
6 | {
7 | "CustomerId": 2,
8 | "PurchasesByMonth": [ 150.60, 150.60, 150.60, 150.60, 150.60, 150.60, 150.60, 150.60, 150.60, 150.60, 150.60, 150.60 ]
9 | },
10 | {
11 | "CustomerId": 3,
12 | "PurchasesByMonth": [ 80.30, 80.30, 80.30, 80.30, 80.30, 80.30, 80.30, 80.30, 80.30, 80.30, 80.30, 80.30 ]
13 | },
14 | {
15 | "CustomerId": 4,
16 | "PurchasesByMonth": [ 130.20, 130.20, 130.20, 130.20, 130.20, 130.20, 130.20, 130.20, 130.20, 130.20, 130.20, 130.20 ]
17 | }
18 | ]
--------------------------------------------------------------------------------
/Completed/Module4/Application/Functions.fs:
--------------------------------------------------------------------------------
1 | module Functions
2 |
3 | open Types
4 | open System
5 | open FSharp.Data
6 |
7 | let tryPromoteToVip purchases =
8 | let customer, amount = purchases
9 | if amount > 100M then { customer with IsVip = true }
10 | else customer
11 |
12 | let [] JsonExample = __SOURCE_DIRECTORY__ + "/Data.json"
13 | type Json = JsonProvider
14 |
15 | let getPurchases customer =
16 | let purchases =
17 | Json.Load "Data.json"
18 | |> Seq.filter (fun c -> c.CustomerId = customer.Id)
19 | |> Seq.collect (fun c -> c.PurchasesByMonth)
20 | |> Seq.average
21 | (customer, purchases)
22 |
23 | let increaseCredit condition customer =
24 | if condition customer then { customer with Credit = customer.Credit + 100M }
25 | else { customer with Credit = customer.Credit + 50M }
26 |
27 | let increaseCreditUsingVip = increaseCredit (fun c -> c.IsVip)
28 |
29 | let upgradeCustomer = getPurchases >> tryPromoteToVip >> increaseCreditUsingVip
30 |
31 | let isAdult customer =
32 | match customer.PersonalDetails with
33 | | None -> false
34 | | Some d -> d.DateOfBirth.AddYears 18 <= DateTime.Now.Date
35 |
36 | let getAlert customer =
37 | match customer.Notifications with
38 | | ReceiveNotifications(receiveAlerts = true) ->
39 | sprintf "Alert for customer %i" customer.Id
40 | | _ -> ""
41 |
42 | let getCustomer id =
43 | let customers = [
44 | { Id = 1; IsVip = false; Credit = 0m; PersonalDetails = Some { FirstName = "John"; LastName = "Doe"; DateOfBirth = DateTime(1980, 1, 1) }; Notifications = NoNotifications }
45 | { Id = 2; IsVip = false; Credit = 10m; PersonalDetails = None; Notifications = ReceiveNotifications(true, true) }
46 | { Id = 3; IsVip = false; Credit = 30m; PersonalDetails = Some { FirstName = "Jane"; LastName = "Jones"; DateOfBirth = DateTime(2010, 2, 2) }; Notifications = ReceiveNotifications(true, false) }
47 | { Id = 4; IsVip = true; Credit = 50m; PersonalDetails = Some { FirstName = "Joe"; LastName = "Smith"; DateOfBirth = DateTime(1986, 3, 3) }; Notifications = ReceiveNotifications(false, true) }
48 | ]
49 | customers
50 | |> List.find (fun c -> c.Id = id)
--------------------------------------------------------------------------------
/Completed/Module4/Application/Program.fs:
--------------------------------------------------------------------------------
1 | module Program
2 |
3 | open System
4 | open Services
5 | open Functions
6 |
7 | []
8 | let rec main args =
9 | let service = CustomerService()
10 | printf "Id to upgrade [1-4]: "
11 | let valid, id = Console.ReadLine () |> Int32.TryParse
12 | printfn ""
13 | if not valid then
14 | printfn "Invalid customer Id"
15 | else
16 | printfn "Customer to upgrade:"
17 | let customerBefore = getCustomer id
18 | customerBefore |> service.GetCustomerInfo |> printfn "%s"
19 | printfn ""
20 | printfn "Upgrading customer..."
21 | let customerAfter = service.UpgradeCustomer id
22 | printfn ""
23 | printfn "Customer upgraded:"
24 | customerAfter |> service.GetCustomerInfo |> printfn "%s"
25 | printfn ""
26 | printfn "Press any key to try again or 'q' to quit"
27 | let input = Console.ReadKey ()
28 | printfn ""
29 | if input.Key = ConsoleKey.Q then 0 else main args
--------------------------------------------------------------------------------
/Completed/Module4/Application/Services.fs:
--------------------------------------------------------------------------------
1 | namespace Services
2 |
3 | namespace Services
4 |
5 | type CustomerService() =
6 | member this.UpgradeCustomer id =
7 | id
8 | |> Functions.getCustomer
9 | |> Functions.upgradeCustomer
10 |
11 | member this.GetCustomerInfo customer =
12 | let isAdult = Functions.isAdult customer
13 | let alert = Functions.getAlert customer
14 | sprintf "Id: %i, IsVip: %b, Credit: %.2f, IsAdult: %b, Alert: %s"
15 | customer.Id customer.IsVip customer.Credit isAdult alert
--------------------------------------------------------------------------------
/Completed/Module4/Application/Try.fsx:
--------------------------------------------------------------------------------
1 | //#r @"/Users/[USERNAME]/.nuget/packages/fsharp.data/3.0.0-beta4/lib/netstandard2.0/FSharp.Data.dll" // Mac / Linux
2 | //#r @"%userprofile%\.nuget\packages\.nuget\packages\fsharp.data\3.0.0-beta4\lib\netstandard2.0\FSharp.Data.dll" // Windows
3 |
4 | #load "Types.fs"
5 | #load "Functions.fs"
6 |
7 | open System
8 | open Types
9 | open Functions
10 |
11 | let customer = {
12 | Id = 1
13 | IsVip = false
14 | Credit = 0M
15 | PersonalDetails = Some {
16 | FirstName = "John"
17 | LastName = "Doe"
18 | DateOfBirth = DateTime(1970, 11, 23)
19 | }
20 | Notifications = ReceiveNotifications(receiveDeals = true, receiveAlerts = true)
21 | }
22 |
23 | let purchases = (customer, 101M)
24 | let vipCustomer = tryPromoteToVip purchases
25 |
26 | let calculatedPurchases = getPurchases customer
27 |
28 | let customerWithMoreCredit = customer |> increaseCredit (fun c -> c.IsVip)
29 |
30 | let upgradedCustomer = upgradeCustomer customer
31 |
32 | let isAdultCustomer = isAdult customer
33 |
34 | let alertCustomer = getAlert customer
--------------------------------------------------------------------------------
/Completed/Module4/Application/Types.fs:
--------------------------------------------------------------------------------
1 | module Types
2 |
3 | open System
4 |
5 | type PersonalDetails = {
6 | FirstName: string
7 | LastName: string
8 | DateOfBirth: DateTime
9 | }
10 |
11 | [] type EUR
12 | [] type USD
13 |
14 | type Notifications =
15 | | NoNotifications
16 | | ReceiveNotifications of receiveDeals: bool * receiveAlerts: bool
17 |
18 | type Customer = {
19 | Id: int
20 | IsVip: bool
21 | Credit: decimal
22 | PersonalDetails: PersonalDetails option
23 | Notifications: Notifications
24 | }
--------------------------------------------------------------------------------
/Completed/Module4/Demo.fsx:
--------------------------------------------------------------------------------
1 | // Demo 1
2 |
3 | let a = 1
4 | let b = 2
5 | let sum x y = x + y
6 | let res = sum a b
7 |
8 | let myTuple = (42, "hello")
9 | let number, message = myTuple
10 |
11 | type MyRecord = { Number: int; Message: string }
12 | let myRecord = { Number = 42; Message = "hello" }
13 | let newRecord = { myRecord with Message = "hi" }
14 |
15 |
16 | // Demo 2
17 |
18 | let compute (x: int) (y: int) (operation: int -> int -> int) = operation x y
19 | let res' = compute 1 2 sum
20 |
21 | let addOne = sum 1
22 | let addTwo = sum 2
23 |
24 | let res1 = addOne 1
25 | let res2 = addTwo res1
26 |
27 | let res2' =
28 | 1
29 | |> addOne
30 | |> addTwo
31 |
32 | let addThree = addOne >> addTwo
33 | let res2'' = addThree 1
34 |
35 |
36 | // Demo 3
37 |
38 | let divide x y =
39 | match y with
40 | | 0 -> None
41 | | _ -> Some(x/y)
42 |
43 | let result = divide 4 2
44 | let result' = divide 4 0
45 |
46 | type DivisionResult =
47 | | DivisionSuccess of result: int
48 | | DivisionError of message: string
49 |
50 | let divide' x y =
51 | match y with
52 | |0 -> DivisionError(message = "Divide by zero")
53 | |_ -> DivisionSuccess(result = x / y)
54 |
55 | let result'' = divide' 4 2
56 | let result''' = divide' 4 0
57 |
58 | [] type m; [] type km; [] type h
59 | let distanceInMts = 11580.0
60 | let distanceInKms = 87.34
61 | //let totalDistance = distanceInMts + distanceInKms // Error
62 |
63 | let convertToKms (mts: float) =
64 | let m = mts / 1.0 // remove unit of measure
65 | let k = m / 1000.0 // convert
66 | k * 1.0 // add new unit of measure
67 |
68 | let convertToKms' (mts: float) = mts / 1000.0 * 1.0
69 |
70 | let convertedToKms = convertToKms distanceInMts
71 | let totalDistance' = convertedToKms + distanceInKms
72 | let speed = totalDistance' / 2.4
73 |
74 |
75 | // Demo 4
76 |
77 | let numbers = [1..100]
78 | let numbersWithZero = 0 :: numbers
79 | let evenNumbers = numbersWithZero |> List.filter (fun x -> x % 2 = 0)
80 |
81 | type MyClass(myField: int) =
82 | member this.MyProperty = myField
83 | member this.MyMethod methodParam = myField + methodParam
84 |
85 | let myInstance = MyClass(1)
86 | myInstance.MyProperty
87 | myInstance.MyMethod 2
88 |
89 | type IMyInterface =
90 | abstract member MyMethod: int -> int
91 |
92 | let myInstance' =
93 | { new IMyInterface with
94 | member this.MyMethod methodParam =
95 | methodParam + 1 }
96 |
97 | myInstance'.MyMethod 2
98 |
99 | //#r @"/Users/[USERNAME]/.nuget/packages/fsharp.data/3.0.0-beta4/lib/netstandard2.0/FSharp.Data.dll" // Mac / Linux
100 | //#r @"%userprofile%\.nuget\packages\.nuget\packages\fsharp.data\3.0.0-beta4\lib\netstandard2.0\FSharp.Data.dll" // Windows
101 |
102 | open FSharp.Data
103 |
104 | type Customer = JsonProvider<"Application/Data.json">
105 | let customers = Customer.Load "Application/Data.json"
106 |
107 | customers
108 | |> Seq.iter (fun r -> printfn "%i: %A" r.CustomerId r.PurchasesByMonth)
--------------------------------------------------------------------------------
/Completed/Module4/Tests/Tests.fs:
--------------------------------------------------------------------------------
1 | module Tests
2 |
3 | open System
4 | open Xunit
5 | open Swensen.Unquote
6 | open Types
7 | open Functions
8 | open Services
9 |
10 | let customer = {
11 | Id = 1
12 | IsVip = false
13 | Credit = 0M
14 | PersonalDetails = Some {
15 | FirstName = "John"
16 | LastName = "Doe"
17 | DateOfBirth = DateTime(1970, 11, 23) }
18 | Notifications = ReceiveNotifications(receiveDeals = true,
19 | receiveAlerts = true) }
20 |
21 |
22 | []
23 | let ``4-1 Get purchases average``() =
24 | let purchases = getPurchases customer
25 | test <@ purchases = (customer, 60M) @>
26 |
27 | []
28 | let ``4-2 Upgrade customer``() =
29 | let service = CustomerService()
30 | let upgradedCustomer = service.UpgradeCustomer 2
31 | test <@ upgradedCustomer.IsVip @>
32 | test <@ upgradedCustomer.Credit = 110M @>
33 |
34 | []
35 | let ``4-3 Get customer info``() =
36 | let service = CustomerService()
37 | let info = service.GetCustomerInfo customer
38 | let expectedInfo = "Id: 1, IsVip: false, Credit: 0.00, IsAdult: true, Alert: Alert for customer 1"
39 | test <@ info = expectedInfo @>
--------------------------------------------------------------------------------
/Completed/Module4/Tests/Tests.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/FSharpWorkshop.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26124.0
5 | MinimumVisualStudioVersion = 15.0.26124.0
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Module1", "Module1", "{6AEBDAFD-25F9-4787-A3C8-5E7631ED71A8}"
7 | ProjectSection(SolutionItems) = preProject
8 | Module1\Demo.fsx = Module1\Demo.fsx
9 | EndProjectSection
10 | EndProject
11 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Application", "Module1\Application\Application.fsproj", "{EBC8203C-8F54-4429-AD28-C0EDD45D9A52}"
12 | EndProject
13 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Tests", "Module1\Tests\Tests.fsproj", "{8EAF483D-535F-4CD6-AC98-4D828543B085}"
14 | EndProject
15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Module2", "Module2", "{3EEDFB7B-016C-480D-A363-DF291DE7D5D2}"
16 | ProjectSection(SolutionItems) = preProject
17 | Module2\Demo.fsx = Module2\Demo.fsx
18 | EndProjectSection
19 | EndProject
20 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Application", "Module2\Application\Application.fsproj", "{7C108125-6659-41BC-9437-EA64164F0CE4}"
21 | EndProject
22 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Tests", "Module2\Tests\Tests.fsproj", "{60535DF9-ABD1-43D9-8411-9E8C3D25ACDF}"
23 | EndProject
24 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Module3", "Module3", "{C2417AC3-A6B8-46BC-83CF-6DFD9517F2DC}"
25 | ProjectSection(SolutionItems) = preProject
26 | Module3\Demo.fsx = Module3\Demo.fsx
27 | EndProjectSection
28 | EndProject
29 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Application", "Module3\Application\Application.fsproj", "{3F557C45-A5C6-487E-9612-96101172874C}"
30 | EndProject
31 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Tests", "Module3\Tests\Tests.fsproj", "{C26CA9FD-FB68-45BB-872C-57D78CA7CE4D}"
32 | EndProject
33 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Module4", "Module4", "{769CCC50-5EB4-40F7-86F2-F6228D0974D0}"
34 | ProjectSection(SolutionItems) = preProject
35 | Module4\Demo.fsx = Module4\Demo.fsx
36 | EndProjectSection
37 | EndProject
38 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Application", "Module4\Application\Application.fsproj", "{B3CABFE5-36F6-4657-9E31-16DD822B0220}"
39 | EndProject
40 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Tests", "Module4\Tests\Tests.fsproj", "{3CEDC805-64E4-4E9C-A98A-6452F7F0F415}"
41 | EndProject
42 | Global
43 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
44 | Debug|Any CPU = Debug|Any CPU
45 | Debug|x64 = Debug|x64
46 | Debug|x86 = Debug|x86
47 | Release|Any CPU = Release|Any CPU
48 | Release|x64 = Release|x64
49 | Release|x86 = Release|x86
50 | EndGlobalSection
51 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
52 | {EBC8203C-8F54-4429-AD28-C0EDD45D9A52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
53 | {EBC8203C-8F54-4429-AD28-C0EDD45D9A52}.Debug|Any CPU.Build.0 = Debug|Any CPU
54 | {EBC8203C-8F54-4429-AD28-C0EDD45D9A52}.Debug|x64.ActiveCfg = Debug|Any CPU
55 | {EBC8203C-8F54-4429-AD28-C0EDD45D9A52}.Debug|x64.Build.0 = Debug|Any CPU
56 | {EBC8203C-8F54-4429-AD28-C0EDD45D9A52}.Debug|x86.ActiveCfg = Debug|Any CPU
57 | {EBC8203C-8F54-4429-AD28-C0EDD45D9A52}.Debug|x86.Build.0 = Debug|Any CPU
58 | {EBC8203C-8F54-4429-AD28-C0EDD45D9A52}.Release|Any CPU.ActiveCfg = Release|Any CPU
59 | {EBC8203C-8F54-4429-AD28-C0EDD45D9A52}.Release|Any CPU.Build.0 = Release|Any CPU
60 | {EBC8203C-8F54-4429-AD28-C0EDD45D9A52}.Release|x64.ActiveCfg = Release|Any CPU
61 | {EBC8203C-8F54-4429-AD28-C0EDD45D9A52}.Release|x64.Build.0 = Release|Any CPU
62 | {EBC8203C-8F54-4429-AD28-C0EDD45D9A52}.Release|x86.ActiveCfg = Release|Any CPU
63 | {EBC8203C-8F54-4429-AD28-C0EDD45D9A52}.Release|x86.Build.0 = Release|Any CPU
64 | {8EAF483D-535F-4CD6-AC98-4D828543B085}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
65 | {8EAF483D-535F-4CD6-AC98-4D828543B085}.Debug|Any CPU.Build.0 = Debug|Any CPU
66 | {8EAF483D-535F-4CD6-AC98-4D828543B085}.Debug|x64.ActiveCfg = Debug|Any CPU
67 | {8EAF483D-535F-4CD6-AC98-4D828543B085}.Debug|x64.Build.0 = Debug|Any CPU
68 | {8EAF483D-535F-4CD6-AC98-4D828543B085}.Debug|x86.ActiveCfg = Debug|Any CPU
69 | {8EAF483D-535F-4CD6-AC98-4D828543B085}.Debug|x86.Build.0 = Debug|Any CPU
70 | {8EAF483D-535F-4CD6-AC98-4D828543B085}.Release|Any CPU.ActiveCfg = Release|Any CPU
71 | {8EAF483D-535F-4CD6-AC98-4D828543B085}.Release|Any CPU.Build.0 = Release|Any CPU
72 | {8EAF483D-535F-4CD6-AC98-4D828543B085}.Release|x64.ActiveCfg = Release|Any CPU
73 | {8EAF483D-535F-4CD6-AC98-4D828543B085}.Release|x64.Build.0 = Release|Any CPU
74 | {8EAF483D-535F-4CD6-AC98-4D828543B085}.Release|x86.ActiveCfg = Release|Any CPU
75 | {8EAF483D-535F-4CD6-AC98-4D828543B085}.Release|x86.Build.0 = Release|Any CPU
76 | {7C108125-6659-41BC-9437-EA64164F0CE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
77 | {7C108125-6659-41BC-9437-EA64164F0CE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
78 | {7C108125-6659-41BC-9437-EA64164F0CE4}.Debug|x64.ActiveCfg = Debug|Any CPU
79 | {7C108125-6659-41BC-9437-EA64164F0CE4}.Debug|x64.Build.0 = Debug|Any CPU
80 | {7C108125-6659-41BC-9437-EA64164F0CE4}.Debug|x86.ActiveCfg = Debug|Any CPU
81 | {7C108125-6659-41BC-9437-EA64164F0CE4}.Debug|x86.Build.0 = Debug|Any CPU
82 | {7C108125-6659-41BC-9437-EA64164F0CE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
83 | {7C108125-6659-41BC-9437-EA64164F0CE4}.Release|Any CPU.Build.0 = Release|Any CPU
84 | {7C108125-6659-41BC-9437-EA64164F0CE4}.Release|x64.ActiveCfg = Release|Any CPU
85 | {7C108125-6659-41BC-9437-EA64164F0CE4}.Release|x64.Build.0 = Release|Any CPU
86 | {7C108125-6659-41BC-9437-EA64164F0CE4}.Release|x86.ActiveCfg = Release|Any CPU
87 | {7C108125-6659-41BC-9437-EA64164F0CE4}.Release|x86.Build.0 = Release|Any CPU
88 | {60535DF9-ABD1-43D9-8411-9E8C3D25ACDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
89 | {60535DF9-ABD1-43D9-8411-9E8C3D25ACDF}.Debug|Any CPU.Build.0 = Debug|Any CPU
90 | {60535DF9-ABD1-43D9-8411-9E8C3D25ACDF}.Debug|x64.ActiveCfg = Debug|Any CPU
91 | {60535DF9-ABD1-43D9-8411-9E8C3D25ACDF}.Debug|x64.Build.0 = Debug|Any CPU
92 | {60535DF9-ABD1-43D9-8411-9E8C3D25ACDF}.Debug|x86.ActiveCfg = Debug|Any CPU
93 | {60535DF9-ABD1-43D9-8411-9E8C3D25ACDF}.Debug|x86.Build.0 = Debug|Any CPU
94 | {60535DF9-ABD1-43D9-8411-9E8C3D25ACDF}.Release|Any CPU.ActiveCfg = Release|Any CPU
95 | {60535DF9-ABD1-43D9-8411-9E8C3D25ACDF}.Release|Any CPU.Build.0 = Release|Any CPU
96 | {60535DF9-ABD1-43D9-8411-9E8C3D25ACDF}.Release|x64.ActiveCfg = Release|Any CPU
97 | {60535DF9-ABD1-43D9-8411-9E8C3D25ACDF}.Release|x64.Build.0 = Release|Any CPU
98 | {60535DF9-ABD1-43D9-8411-9E8C3D25ACDF}.Release|x86.ActiveCfg = Release|Any CPU
99 | {60535DF9-ABD1-43D9-8411-9E8C3D25ACDF}.Release|x86.Build.0 = Release|Any CPU
100 | {3F557C45-A5C6-487E-9612-96101172874C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
101 | {3F557C45-A5C6-487E-9612-96101172874C}.Debug|Any CPU.Build.0 = Debug|Any CPU
102 | {3F557C45-A5C6-487E-9612-96101172874C}.Debug|x64.ActiveCfg = Debug|Any CPU
103 | {3F557C45-A5C6-487E-9612-96101172874C}.Debug|x64.Build.0 = Debug|Any CPU
104 | {3F557C45-A5C6-487E-9612-96101172874C}.Debug|x86.ActiveCfg = Debug|Any CPU
105 | {3F557C45-A5C6-487E-9612-96101172874C}.Debug|x86.Build.0 = Debug|Any CPU
106 | {3F557C45-A5C6-487E-9612-96101172874C}.Release|Any CPU.ActiveCfg = Release|Any CPU
107 | {3F557C45-A5C6-487E-9612-96101172874C}.Release|Any CPU.Build.0 = Release|Any CPU
108 | {3F557C45-A5C6-487E-9612-96101172874C}.Release|x64.ActiveCfg = Release|Any CPU
109 | {3F557C45-A5C6-487E-9612-96101172874C}.Release|x64.Build.0 = Release|Any CPU
110 | {3F557C45-A5C6-487E-9612-96101172874C}.Release|x86.ActiveCfg = Release|Any CPU
111 | {3F557C45-A5C6-487E-9612-96101172874C}.Release|x86.Build.0 = Release|Any CPU
112 | {C26CA9FD-FB68-45BB-872C-57D78CA7CE4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
113 | {C26CA9FD-FB68-45BB-872C-57D78CA7CE4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
114 | {C26CA9FD-FB68-45BB-872C-57D78CA7CE4D}.Debug|x64.ActiveCfg = Debug|Any CPU
115 | {C26CA9FD-FB68-45BB-872C-57D78CA7CE4D}.Debug|x64.Build.0 = Debug|Any CPU
116 | {C26CA9FD-FB68-45BB-872C-57D78CA7CE4D}.Debug|x86.ActiveCfg = Debug|Any CPU
117 | {C26CA9FD-FB68-45BB-872C-57D78CA7CE4D}.Debug|x86.Build.0 = Debug|Any CPU
118 | {C26CA9FD-FB68-45BB-872C-57D78CA7CE4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
119 | {C26CA9FD-FB68-45BB-872C-57D78CA7CE4D}.Release|Any CPU.Build.0 = Release|Any CPU
120 | {C26CA9FD-FB68-45BB-872C-57D78CA7CE4D}.Release|x64.ActiveCfg = Release|Any CPU
121 | {C26CA9FD-FB68-45BB-872C-57D78CA7CE4D}.Release|x64.Build.0 = Release|Any CPU
122 | {C26CA9FD-FB68-45BB-872C-57D78CA7CE4D}.Release|x86.ActiveCfg = Release|Any CPU
123 | {C26CA9FD-FB68-45BB-872C-57D78CA7CE4D}.Release|x86.Build.0 = Release|Any CPU
124 | {B3CABFE5-36F6-4657-9E31-16DD822B0220}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
125 | {B3CABFE5-36F6-4657-9E31-16DD822B0220}.Debug|Any CPU.Build.0 = Debug|Any CPU
126 | {B3CABFE5-36F6-4657-9E31-16DD822B0220}.Debug|x64.ActiveCfg = Debug|Any CPU
127 | {B3CABFE5-36F6-4657-9E31-16DD822B0220}.Debug|x64.Build.0 = Debug|Any CPU
128 | {B3CABFE5-36F6-4657-9E31-16DD822B0220}.Debug|x86.ActiveCfg = Debug|Any CPU
129 | {B3CABFE5-36F6-4657-9E31-16DD822B0220}.Debug|x86.Build.0 = Debug|Any CPU
130 | {B3CABFE5-36F6-4657-9E31-16DD822B0220}.Release|Any CPU.ActiveCfg = Release|Any CPU
131 | {B3CABFE5-36F6-4657-9E31-16DD822B0220}.Release|Any CPU.Build.0 = Release|Any CPU
132 | {B3CABFE5-36F6-4657-9E31-16DD822B0220}.Release|x64.ActiveCfg = Release|Any CPU
133 | {B3CABFE5-36F6-4657-9E31-16DD822B0220}.Release|x64.Build.0 = Release|Any CPU
134 | {B3CABFE5-36F6-4657-9E31-16DD822B0220}.Release|x86.ActiveCfg = Release|Any CPU
135 | {B3CABFE5-36F6-4657-9E31-16DD822B0220}.Release|x86.Build.0 = Release|Any CPU
136 | {3CEDC805-64E4-4E9C-A98A-6452F7F0F415}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
137 | {3CEDC805-64E4-4E9C-A98A-6452F7F0F415}.Debug|Any CPU.Build.0 = Debug|Any CPU
138 | {3CEDC805-64E4-4E9C-A98A-6452F7F0F415}.Debug|x64.ActiveCfg = Debug|Any CPU
139 | {3CEDC805-64E4-4E9C-A98A-6452F7F0F415}.Debug|x64.Build.0 = Debug|Any CPU
140 | {3CEDC805-64E4-4E9C-A98A-6452F7F0F415}.Debug|x86.ActiveCfg = Debug|Any CPU
141 | {3CEDC805-64E4-4E9C-A98A-6452F7F0F415}.Debug|x86.Build.0 = Debug|Any CPU
142 | {3CEDC805-64E4-4E9C-A98A-6452F7F0F415}.Release|Any CPU.ActiveCfg = Release|Any CPU
143 | {3CEDC805-64E4-4E9C-A98A-6452F7F0F415}.Release|Any CPU.Build.0 = Release|Any CPU
144 | {3CEDC805-64E4-4E9C-A98A-6452F7F0F415}.Release|x64.ActiveCfg = Release|Any CPU
145 | {3CEDC805-64E4-4E9C-A98A-6452F7F0F415}.Release|x64.Build.0 = Release|Any CPU
146 | {3CEDC805-64E4-4E9C-A98A-6452F7F0F415}.Release|x86.ActiveCfg = Release|Any CPU
147 | {3CEDC805-64E4-4E9C-A98A-6452F7F0F415}.Release|x86.Build.0 = Release|Any CPU
148 | EndGlobalSection
149 | GlobalSection(SolutionProperties) = preSolution
150 | HideSolutionNode = FALSE
151 | EndGlobalSection
152 | GlobalSection(NestedProjects) = preSolution
153 | {EBC8203C-8F54-4429-AD28-C0EDD45D9A52} = {6AEBDAFD-25F9-4787-A3C8-5E7631ED71A8}
154 | {8EAF483D-535F-4CD6-AC98-4D828543B085} = {6AEBDAFD-25F9-4787-A3C8-5E7631ED71A8}
155 | {7C108125-6659-41BC-9437-EA64164F0CE4} = {3EEDFB7B-016C-480D-A363-DF291DE7D5D2}
156 | {60535DF9-ABD1-43D9-8411-9E8C3D25ACDF} = {3EEDFB7B-016C-480D-A363-DF291DE7D5D2}
157 | {3F557C45-A5C6-487E-9612-96101172874C} = {C2417AC3-A6B8-46BC-83CF-6DFD9517F2DC}
158 | {C26CA9FD-FB68-45BB-872C-57D78CA7CE4D} = {C2417AC3-A6B8-46BC-83CF-6DFD9517F2DC}
159 | {B3CABFE5-36F6-4657-9E31-16DD822B0220} = {769CCC50-5EB4-40F7-86F2-F6228D0974D0}
160 | {3CEDC805-64E4-4E9C-A98A-6452F7F0F415} = {769CCC50-5EB4-40F7-86F2-F6228D0974D0}
161 | EndGlobalSection
162 | GlobalSection(ExtensibilityGlobals) = postSolution
163 | SolutionGuid = {4EDD1774-6287-4F9B-BB5A-24ADCDDCD1E8}
164 | EndGlobalSection
165 | EndGlobal
166 |
--------------------------------------------------------------------------------
/FSharpWorkshop_Exercises.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jorgef/fsharpworkshop/f06b9e0089bb2d13e953b30fdcd1a1a01adf1698/FSharpWorkshop_Exercises.docx
--------------------------------------------------------------------------------
/FSharpWorkshop_Exercises.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jorgef/fsharpworkshop/f06b9e0089bb2d13e953b30fdcd1a1a01adf1698/FSharpWorkshop_Exercises.pdf
--------------------------------------------------------------------------------
/FSharpWorkshop_Slides.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jorgef/fsharpworkshop/f06b9e0089bb2d13e953b30fdcd1a1a01adf1698/FSharpWorkshop_Slides.pdf
--------------------------------------------------------------------------------
/FSharpWorkshop_Slides.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jorgef/fsharpworkshop/f06b9e0089bb2d13e953b30fdcd1a1a01adf1698/FSharpWorkshop_Slides.pptx
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/Module1/Application/Application.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Module1/Application/Functions.fs:
--------------------------------------------------------------------------------
1 | module Functions
2 |
3 | open Types
4 |
5 |
--------------------------------------------------------------------------------
/Module1/Application/Try.fsx:
--------------------------------------------------------------------------------
1 | #load "Types.fs"
2 | #load "Functions.fs"
3 |
4 | open Types
5 | open Functions
6 |
7 |
--------------------------------------------------------------------------------
/Module1/Application/Types.fs:
--------------------------------------------------------------------------------
1 | module Types
2 |
3 |
--------------------------------------------------------------------------------
/Module1/Demo.fsx:
--------------------------------------------------------------------------------
1 | // Demo 1
2 |
3 | let a = 1
4 | let b = 2
5 | let sum x y = x + y
6 | let res = sum a b
7 |
8 | let myTuple = (42, "hello")
9 | let number, message = myTuple
10 |
11 | type MyRecord = { Number: int; Message: string }
12 | let myRecord = { Number = 42; Message = "hello" }
13 | let newRecord = { myRecord with Message = "hi" }
--------------------------------------------------------------------------------
/Module1/Tests/Tests.fs:
--------------------------------------------------------------------------------
1 | module Tests
2 |
3 | open Xunit
4 | open Swensen.Unquote
5 | open Types
6 | open Functions
7 |
8 | //[]
9 | //let ``1-1 Create customer``() =
10 | // let customer = { Id = 1; IsVip = false; Credit = 0M }
11 | // test <@ customer.GetType () = typeof @>
12 | //
13 | //[]
14 | //let ``1-2 Promote to vip``() =
15 | // let customer = { Id = 1; IsVip = false; Credit = 0M }
16 | // let promotedCustomer = tryPromoteToVip (customer, 100.1M)
17 | // test <@ promotedCustomer.IsVip = true @>
18 | //
19 | //[]
20 | //let ``1-3 Do not promote to vip``() =
21 | // let customer = { Id = 1; IsVip = false; Credit = 0M }
22 | // let promotedCustomer = tryPromoteToVip (customer, 99.9M)
23 | // test <@ promotedCustomer.IsVip = false @>
24 | //
25 | //[]
26 | //let ``1-4 Get purchases for odd customers``() =
27 | // let customer = { Id = 1; IsVip = false; Credit = 0M }
28 | // let _, purchases = getPurchases customer
29 | // test <@ purchases = 80M @>
30 | //
31 | //[]
32 | //let ``1-5 Get purchases for even customers``() =
33 | // let customer = { Id = 2; IsVip = false; Credit = 0M }
34 | // let _, purchases = getPurchases customer
35 | // test <@ purchases = 120M @>
--------------------------------------------------------------------------------
/Module1/Tests/Tests.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Module2/Application/Application.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Module2/Application/Functions.fs:
--------------------------------------------------------------------------------
1 | module Functions
2 |
3 | open Types
4 |
5 | let tryPromoteToVip purchases =
6 | let customer, amount = purchases
7 | if amount > 100M then { customer with IsVip = true }
8 | else customer
9 |
10 | let getPurchases customer =
11 | if customer.Id % 2 = 0 then (customer, 120M)
12 | else (customer, 80M)
--------------------------------------------------------------------------------
/Module2/Application/Try.fsx:
--------------------------------------------------------------------------------
1 | #load "Types.fs"
2 | #load "Functions.fs"
3 |
4 | open Types
5 | open Functions
6 |
7 | let customer = { Id = 1; IsVip = false; Credit = 10M }
8 |
9 | let purchases = (customer, 101M)
10 | let vipCustomer = tryPromoteToVip purchases
11 |
12 | let calculatedPurchases = getPurchases customer
--------------------------------------------------------------------------------
/Module2/Application/Types.fs:
--------------------------------------------------------------------------------
1 | module Types
2 |
3 | type Customer = {
4 | Id: int
5 | IsVip: bool
6 | Credit: decimal
7 | }
--------------------------------------------------------------------------------
/Module2/Demo.fsx:
--------------------------------------------------------------------------------
1 | // Demo 1
2 |
3 | let a = 1
4 | let b = 2
5 | let sum x y = x + y
6 | let res = sum a b
7 |
8 | let myTuple = (42, "hello")
9 | let number, message = myTuple
10 |
11 | type MyRecord = { Number: int; Message: string }
12 | let myRecord = { Number = 42; Message = "hello" }
13 | let newRecord = { myRecord with Message = "hi" }
14 |
15 |
16 | // Demo 2
17 |
18 | let compute (x: int) (y: int) (operation: int -> int -> int) = operation x y
19 | let res' = compute 1 2 sum
20 |
21 | let addOne = sum 1
22 | let addTwo = sum 2
23 |
24 | let res1 = addOne 1
25 | let res2 = addTwo res1
26 |
27 | let res2' =
28 | 1
29 | |> addOne
30 | |> addTwo
31 |
32 | let addThree = addOne >> addTwo
33 | let res2'' = addThree 1
--------------------------------------------------------------------------------
/Module2/Tests/Tests.fs:
--------------------------------------------------------------------------------
1 | module Tests
2 |
3 | open Xunit
4 | open Swensen.Unquote
5 | open Types
6 | open Functions
7 |
8 | //[]
9 | //let ``2-1 Increase min credit using id``() =
10 | // let customer = { Id = 1; IsVip = false; Credit = 0M }
11 | // let upgradedCustomer = increaseCredit (fun c -> c.Id = 2) customer
12 | // test <@ upgradedCustomer.Credit = 50M @>
13 | //
14 | //[]
15 | //let ``2-2 Increase max credit using id``() =
16 | // let customer = { Id = 2; IsVip = false; Credit = 0M }
17 | // let upgradedCustomer = increaseCredit (fun c -> c.Id = 2) customer
18 | // test <@ upgradedCustomer.Credit = 100M @>
19 | //
20 | //[]
21 | //let ``2-3 Increase credit keeping existing one``() =
22 | // let customer = { Id = 2; IsVip = false; Credit = 10M }
23 | // let upgradedCustomer = increaseCredit (fun c -> c.Id = 2) customer
24 | // test <@ upgradedCustomer.Credit = 110M @>
25 | //
26 | //[]
27 | //let ``2-4 Increase max credit using vip``() =
28 | // let customer = { Id = 2; IsVip = true; Credit = 0M }
29 | // let upgradedCustomer = increaseCreditUsingVip customer
30 | // test <@ upgradedCustomer.Credit = 100M @>
31 | //
32 | //[]
33 | //let ``2-5 Upgrade customer with even id``() =
34 | // let customer = { Id = 2; IsVip = false; Credit = 0M }
35 | // let upgradedCustomer = upgradeCustomer customer
36 | // test <@ upgradedCustomer.IsVip = true && upgradedCustomer.Credit = 100M @>
37 | //
38 | //[]
39 | //let ``2-6 Upgrade customer with odd id``() =
40 | // let customer = { Id = 1; IsVip = false; Credit = 0M }
41 | // let upgradedCustomer = upgradeCustomer customer
42 | // test <@ upgradedCustomer.IsVip = false && upgradedCustomer.Credit = 50M @>
--------------------------------------------------------------------------------
/Module2/Tests/Tests.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Module3/Application/Application.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Module3/Application/Functions.fs:
--------------------------------------------------------------------------------
1 | module Functions
2 |
3 | open Types
4 | open System
5 |
6 | let tryPromoteToVip purchases =
7 | let customer, amount = purchases
8 | if amount > 100M then { customer with IsVip = true }
9 | else customer
10 |
11 | let getPurchases customer =
12 | if customer.Id % 2 = 0 then (customer, 120M)
13 | else (customer, 80M)
14 |
15 | let increaseCredit condition customer =
16 | if condition customer then { customer with Credit = customer.Credit + 100M }
17 | else { customer with Credit = customer.Credit + 50M }
18 |
19 | let increaseCreditUsingVip = increaseCredit (fun c -> c.IsVip)
20 |
21 | let upgradeCustomer = getPurchases >> tryPromoteToVip >> increaseCreditUsingVip
--------------------------------------------------------------------------------
/Module3/Application/Try.fsx:
--------------------------------------------------------------------------------
1 | #load "Types.fs"
2 | #load "Functions.fs"
3 |
4 | open System
5 | open Types
6 | open Functions
7 |
8 | let customer = {
9 | Id = 1
10 | IsVip = false
11 | Credit = 0M
12 | PersonalDetails = Some {
13 | FirstName = "John"
14 | LastName = "Doe"
15 | DateOfBirth = DateTime(1970, 11, 23)
16 | }
17 | Notifications = ReceiveNotifications(receiveDeals = true, receiveAlerts = true)
18 | }
19 |
20 | let purchases = (customer, 101M)
21 | let vipCustomer = tryPromoteToVip purchases
22 |
23 | let calculatedPurchases = getPurchases customer
24 |
25 | let customerWithMoreCredit = customer |> increaseCredit (fun c -> c.IsVip)
26 |
27 | let upgradedCustomer = upgradeCustomer customer
--------------------------------------------------------------------------------
/Module3/Application/Types.fs:
--------------------------------------------------------------------------------
1 | module Types
2 |
3 | open System
4 |
5 | type Customer = {
6 | Id: int
7 | IsVip: bool
8 | Credit: decimal
9 | }
--------------------------------------------------------------------------------
/Module3/Demo.fsx:
--------------------------------------------------------------------------------
1 | // Demo 1
2 |
3 | let a = 1
4 | let b = 2
5 | let sum x y = x + y
6 | let res = sum a b
7 |
8 | let myTuple = (42, "hello")
9 | let number, message = myTuple
10 |
11 | type MyRecord = { Number: int; Message: string }
12 | let myRecord = { Number = 42; Message = "hello" }
13 | let newRecord = { myRecord with Message = "hi" }
14 |
15 |
16 | // Demo 2
17 |
18 | let compute (x: int) (y: int) (operation: int -> int -> int) = operation x y
19 | let res' = compute 1 2 sum
20 |
21 | let addOne = sum 1
22 | let addTwo = sum 2
23 |
24 | let res1 = addOne 1
25 | let res2 = addTwo res1
26 |
27 | let res2' =
28 | 1
29 | |> addOne
30 | |> addTwo
31 |
32 | let addThree = addOne >> addTwo
33 | let res2'' = addThree 1
34 |
35 |
36 | // Demo 3
37 |
38 | let divide x y =
39 | match y with
40 | | 0 -> None
41 | | _ -> Some(x/y)
42 |
43 | let result = divide 4 2
44 | let result' = divide 4 0
45 |
46 | type DivisionResult =
47 | | DivisionSuccess of result: int
48 | | DivisionError of message: string
49 |
50 | let divide' x y =
51 | match y with
52 | |0 -> DivisionError(message = "Divide by zero")
53 | |_ -> DivisionSuccess(result = x / y)
54 |
55 | let result'' = divide' 4 2
56 | let result''' = divide' 4 0
57 |
58 | [] type m; [] type km; [] type h
59 | let distanceInMts = 11580.0
60 | let distanceInKms = 87.34
61 | //let totalDistance = distanceInMts + distanceInKms // Error
62 |
63 | let convertToKms (mts: float) =
64 | let m = mts / 1.0 // remove unit of measure
65 | let k = m / 1000.0 // convert
66 | k * 1.0 // add new unit of measure
67 |
68 | let convertToKms' (mts: float) = mts / 1000.0 * 1.0
69 |
70 | let convertedToKms = convertToKms distanceInMts
71 | let totalDistance' = convertedToKms + distanceInKms
72 | let speed = totalDistance' / 2.4
--------------------------------------------------------------------------------
/Module3/Tests/Tests.fs:
--------------------------------------------------------------------------------
1 | module Tests
2 |
3 | open System
4 | open Xunit
5 | open Swensen.Unquote
6 | open Types
7 | open Functions
8 |
9 | //let customer = {
10 | // Id = 1
11 | // IsVip = false
12 | // Credit = 0M
13 | // PersonalDetails = Some {
14 | // FirstName = "John"
15 | // LastName = "Doe"
16 | // DateOfBirth = DateTime(1970, 11, 23) }
17 | // Notifications = ReceiveNotifications(receiveDeals = true,
18 | // receiveAlerts = true)
19 | //}
20 | //
21 | //[]
22 | //let ``3-1 Create customer``() =
23 | // test <@ customer.GetType() = typeof @>
24 | //
25 | //[]
26 | //let ``3-2 Increase credit using USD``() =
27 | // let upgradedCustomer = increaseCreditUsingVip customer
28 | // test <@ upgradedCustomer.Credit = 50M @>
29 | //
30 | //[]
31 | //let ``3-3 Adult customer``() =
32 | // test <@ customer |> isAdult @>
33 | //
34 | //[]
35 | //let ``3-4 Non-adult customer``() =
36 | // let nonadult = { customer with PersonalDetails = Some { customer.PersonalDetails.Value with DateOfBirth = DateTime.Now.AddYears(-1) } }
37 | // test <@ not (nonadult |> isAdult) @>
38 | //
39 | //[]
40 | //let ``3-5 Customer without personal details``() =
41 | // let nonadult = { customer with PersonalDetails = None }
42 | // test <@ not (nonadult |> isAdult) @>
43 | //
44 | //[]
45 | //let ``3-6 Get alert when nofications are enabled``() =
46 | // let alert = customer |> getAlert
47 | // test <@ alert = "Alert for customer 1" @>
48 | //
49 | //[]
50 | //let ``3-7 Do not get alert when nofications are disabled``() =
51 | // let alert = { customer with Notifications = NoNotifications } |> getAlert
52 | // test <@ alert = "" @>
--------------------------------------------------------------------------------
/Module3/Tests/Tests.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Module4/Application/Application.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 | Exe
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Always
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Module4/Application/Data.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "CustomerId": 1,
4 | "PurchasesByMonth": [ 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0 ]
5 | },
6 | {
7 | "CustomerId": 2,
8 | "PurchasesByMonth": [ 150.60, 150.60, 150.60, 150.60, 150.60, 150.60, 150.60, 150.60, 150.60, 150.60, 150.60, 150.60 ]
9 | },
10 | {
11 | "CustomerId": 3,
12 | "PurchasesByMonth": [ 80.30, 80.30, 80.30, 80.30, 80.30, 80.30, 80.30, 80.30, 80.30, 80.30, 80.30, 80.30 ]
13 | },
14 | {
15 | "CustomerId": 4,
16 | "PurchasesByMonth": [ 130.20, 130.20, 130.20, 130.20, 130.20, 130.20, 130.20, 130.20, 130.20, 130.20, 130.20, 130.20 ]
17 | }
18 | ]
--------------------------------------------------------------------------------
/Module4/Application/Functions.fs:
--------------------------------------------------------------------------------
1 | module Functions
2 |
3 | open Types
4 | open System
5 |
6 | let tryPromoteToVip purchases =
7 | let customer, amount = purchases
8 | if amount > 100M then { customer with IsVip = true }
9 | else customer
10 |
11 | let getPurchases customer =
12 | if customer.Id % 2 = 0 then (customer, 120M)
13 | else (customer, 80M)
14 |
15 | let increaseCredit condition customer =
16 | if condition customer then { customer with Credit = customer.Credit + 100M }
17 | else { customer with Credit = customer.Credit + 50M }
18 |
19 | let increaseCreditUsingVip = increaseCredit (fun c -> c.IsVip)
20 |
21 | let upgradeCustomer = getPurchases >> tryPromoteToVip >> increaseCreditUsingVip
22 |
23 | let isAdult customer =
24 | match customer.PersonalDetails with
25 | | None -> false
26 | | Some d -> d.DateOfBirth.AddYears 18 <= DateTime.Now.Date
27 |
28 | let getAlert customer =
29 | match customer.Notifications with
30 | | ReceiveNotifications(receiveAlerts = true) ->
31 | sprintf "Alert for customer %i" customer.Id
32 | | _ -> ""
33 |
34 | let getCustomer id =
35 | let customers = [
36 | { Id = 1; IsVip = false; Credit = 0m; PersonalDetails = Some { FirstName = "John"; LastName = "Doe"; DateOfBirth = DateTime(1980, 1, 1) }; Notifications = NoNotifications }
37 | { Id = 2; IsVip = false; Credit = 10m; PersonalDetails = None; Notifications = ReceiveNotifications(true, true) }
38 | { Id = 3; IsVip = false; Credit = 30m; PersonalDetails = Some { FirstName = "Jane"; LastName = "Jones"; DateOfBirth = DateTime(2010, 2, 2) }; Notifications = ReceiveNotifications(true, false) }
39 | { Id = 4; IsVip = true; Credit = 50m; PersonalDetails = Some { FirstName = "Joe"; LastName = "Smith"; DateOfBirth = DateTime(1986, 3, 3) }; Notifications = ReceiveNotifications(false, true) }
40 | ]
41 | customers
42 | |> List.find (fun c -> c.Id = id)
--------------------------------------------------------------------------------
/Module4/Application/Program.fs:
--------------------------------------------------------------------------------
1 | module Program
2 |
3 | open System
4 | open Services
5 | open Functions
6 |
7 | []
8 | let rec main args =
9 | // let service = CustomerService()
10 | // printf "Id to upgrade [1-4]: "
11 | // let valid, id = Console.ReadLine () |> Int32.TryParse
12 | // printfn ""
13 | // if not valid then
14 | // printfn "Invalid customer Id"
15 | // else
16 | // printfn "Customer to upgrade:"
17 | // let customerBefore = getCustomer id
18 | // customerBefore |> service.GetCustomerInfo |> printfn "%s"
19 | // printfn ""
20 | // printfn "Upgrading customer..."
21 | // let customerAfter = service.UpgradeCustomer id
22 | // printfn ""
23 | // printfn "Customer upgraded:"
24 | // customerAfter |> service.GetCustomerInfo |> printfn "%s"
25 | printfn ""
26 | printfn "Press any key to try again or 'q' to quit"
27 | let input = Console.ReadKey ()
28 | printfn ""
29 | if input.Key = ConsoleKey.Q then 0 else main args
30 |
--------------------------------------------------------------------------------
/Module4/Application/Services.fs:
--------------------------------------------------------------------------------
1 | namespace Services
2 |
--------------------------------------------------------------------------------
/Module4/Application/Try.fsx:
--------------------------------------------------------------------------------
1 | //#r @"/Users/[USERNAME]/.nuget/packages/fsharp.data/3.0.0-beta4/lib/netstandard2.0/FSharp.Data.dll" // Mac / Linux
2 | //#r @"%userprofile%\.nuget\packages\.nuget\packages\fsharp.data\3.0.0-beta4\lib\netstandard2.0\FSharp.Data.dll" // Windows
3 | #load "Types.fs"
4 | #load "Functions.fs"
5 |
6 | open System
7 | open Types
8 | open Functions
9 |
10 | let customer = {
11 | Id = 1
12 | IsVip = false
13 | Credit = 0M
14 | PersonalDetails = Some {
15 | FirstName = "John"
16 | LastName = "Doe"
17 | DateOfBirth = DateTime(1970, 11, 23)
18 | }
19 | Notifications = ReceiveNotifications(receiveDeals = true, receiveAlerts = true)
20 | }
21 |
22 | let purchases = (customer, 101M)
23 | let vipCustomer = tryPromoteToVip purchases
24 |
25 | let calculatedPurchases = getPurchases customer
26 |
27 | let customerWithMoreCredit = customer |> increaseCredit (fun c -> c.IsVip)
28 |
29 | let upgradedCustomer = upgradeCustomer customer
30 |
31 | let isAdultCustomer = isAdult customer
32 |
33 | let alertCustomer = getAlert customer
34 |
--------------------------------------------------------------------------------
/Module4/Application/Types.fs:
--------------------------------------------------------------------------------
1 | module Types
2 |
3 | open System
4 |
5 | type PersonalDetails = {
6 | FirstName: string
7 | LastName: string
8 | DateOfBirth: DateTime
9 | }
10 |
11 | [] type EUR
12 | [] type USD
13 |
14 | type Notifications =
15 | | NoNotifications
16 | | ReceiveNotifications of receiveDeals: bool * receiveAlerts: bool
17 |
18 | type Customer = {
19 | Id: int
20 | IsVip: bool
21 | Credit: decimal
22 | PersonalDetails: PersonalDetails option
23 | Notifications: Notifications
24 | }
--------------------------------------------------------------------------------
/Module4/Application/paket.references:
--------------------------------------------------------------------------------
1 | FSharp.Data
--------------------------------------------------------------------------------
/Module4/Demo.fsx:
--------------------------------------------------------------------------------
1 | // Demo 1
2 |
3 | let a = 1
4 | let b = 2
5 | let sum x y = x + y
6 | let res = sum a b
7 |
8 | let myTuple = (42, "hello")
9 | let number, message = myTuple
10 |
11 | type MyRecord = { Number: int; Message: string }
12 | let myRecord = { Number = 42; Message = "hello" }
13 | let newRecord = { myRecord with Message = "hi" }
14 |
15 |
16 | // Demo 2
17 |
18 | let compute (x: int) (y: int) (operation: int -> int -> int) = operation x y
19 | let res' = compute 1 2 sum
20 |
21 | let addOne = sum 1
22 | let addTwo = sum 2
23 |
24 | let res1 = addOne 1
25 | let res2 = addTwo res1
26 |
27 | let res2' =
28 | 1
29 | |> addOne
30 | |> addTwo
31 |
32 | let addThree = addOne >> addTwo
33 | let res2'' = addThree 1
34 |
35 |
36 | // Demo 3
37 |
38 | let divide x y =
39 | match y with
40 | | 0 -> None
41 | | _ -> Some(x/y)
42 |
43 | let result = divide 4 2
44 | let result' = divide 4 0
45 |
46 | type DivisionResult =
47 | | DivisionSuccess of result: int
48 | | DivisionError of message: string
49 |
50 | let divide' x y =
51 | match y with
52 | |0 -> DivisionError(message = "Divide by zero")
53 | |_ -> DivisionSuccess(result = x / y)
54 |
55 | let result'' = divide' 4 2
56 | let result''' = divide' 4 0
57 |
58 | [] type m; [] type km; [] type h
59 | let distanceInMts = 11580.0
60 | let distanceInKms = 87.34
61 | //let totalDistance = distanceInMts + distanceInKms // Error
62 |
63 | let convertToKms (mts: float) =
64 | let m = mts / 1.0 // remove unit of measure
65 | let k = m / 1000.0 // convert
66 | k * 1.0 // add new unit of measure
67 |
68 | let convertToKms' (mts: float) = mts / 1000.0 * 1.0
69 |
70 | let convertedToKms = convertToKms distanceInMts
71 | let totalDistance' = convertedToKms + distanceInKms
72 | let speed = totalDistance' / 2.4
73 |
74 |
75 | // Demo 4
76 |
77 | let numbers = [1..100]
78 | let numbersWithZero = 0 :: numbers
79 | let evenNumbers = numbersWithZero |> List.filter (fun x -> x % 2 = 0)
80 |
81 | type MyClass(myField: int) =
82 | member this.MyProperty = myField
83 | member this.MyMethod methodParam = myField + methodParam
84 |
85 | let myInstance = MyClass(1)
86 | myInstance.MyProperty
87 | myInstance.MyMethod 2
88 |
89 | type IMyInterface =
90 | abstract member MyMethod: int -> int
91 |
92 | let myInstance' =
93 | { new IMyInterface with
94 | member this.MyMethod methodParam =
95 | methodParam + 1 }
96 |
97 | myInstance'.MyMethod 2
98 |
99 | //#r @"/Users/[USERNAME]/.nuget/packages/fsharp.data/3.0.0-beta4/lib/netstandard2.0/FSharp.Data.dll" // Mac / Linux
100 | //#r @"%userprofile%\.nuget\packages\.nuget\packages\fsharp.data\3.0.0-beta4\lib\netstandard2.0\FSharp.Data.dll" // Windows
101 |
102 | open FSharp.Data
103 |
104 | type Customer = JsonProvider<"Data.json">
105 | let customers = Customer.Load "Data.json"
106 |
107 | customers
108 | |> Seq.iter (fun r -> printfn "%i: %A" r.CustomerId r.PurchasesByMonth)
--------------------------------------------------------------------------------
/Module4/Tests/Tests.fs:
--------------------------------------------------------------------------------
1 | module Tests
2 |
3 | open System
4 | open Xunit
5 | open Swensen.Unquote
6 | open Types
7 | open Functions
8 | open Services
9 |
10 | let customer = {
11 | Id = 1
12 | IsVip = false
13 | Credit = 0M
14 | PersonalDetails = Some {
15 | FirstName = "John"
16 | LastName = "Doe"
17 | DateOfBirth = DateTime(1970, 11, 23) }
18 | Notifications = ReceiveNotifications(receiveDeals = true,
19 | receiveAlerts = true) }
20 |
21 |
22 | //[]
23 | //let ``4-1 Get purchases average``() =
24 | // let purchases = getPurchases customer
25 | // test <@ purchases = (customer, 60M) @>
26 | //
27 | //[]
28 | //let ``4-2 Upgrade customer``() =
29 | // let service = CustomerService()
30 | // let upgradedCustomer = service.UpgradeCustomer 2
31 | // test <@ upgradedCustomer.IsVip @>
32 | // test <@ upgradedCustomer.Credit = 110M @>
33 | //
34 | //[]
35 | //let ``4-3 Get customer info``() =
36 | // let service = CustomerService()
37 | // let info = service.GetCustomerInfo customer
38 | // let expectedInfo = "Id: 1, IsVip: false, Credit: 0.00, IsAdult: true, Alert: Alert for customer 1"
39 | // test <@ info = expectedInfo @>
--------------------------------------------------------------------------------
/Module4/Tests/Tests.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # F# Workshop
2 |
3 | ## Introduction
4 |
5 | **Do you want to learn F# and Functional Programming?** Well, you better start coding!
6 |
7 | Learning a new programming language is not easy, on top of reading a lot you need to practice even more.
8 |
9 | This workshop is designed to teach you some of the basics of F# and Functional Programming by combining theory ([slides](https://github.com/jorgef/fsharpworkshop/raw/master/FSharpWorkshop_Slides.pptx)) and practice ([exercises](https://github.com/jorgef/fsharpworkshop/raw/master/FSharpWorkshop_Exercises.pdf)).
10 |
11 | ## Modules
12 |
13 | The course is split into 4 modules, each of them contains a presentation (theory) and one exercise (practice).
14 |
15 | ### Module 1
16 |
17 | - Bindings
18 | - Functions
19 | - Tuples
20 | - Records
21 |
22 | ### Module 2
23 |
24 | - High order functions
25 | - Pipelining
26 | - Partial application
27 | - Composition
28 |
29 | ### Module 3
30 |
31 | - Options
32 | - Pattern matching
33 | - Discriminated unions
34 | - Units of measure
35 |
36 | ### Module 4
37 |
38 | - Functional lists
39 | - Object-oriented programming
40 | - Type providers
41 |
42 | ## Pre-requisites
43 |
44 | - [.NET Core SDK](https://www.microsoft.com/net/download)
45 | - [Visual Studio Code](https://code.visualstudio.com/)
46 | - [Ionide Package](http://ionide.io/)
47 | - [Mono (Mac only)](https://www.mono-project.com/download/stable/)
48 | - [Mono, F# Compiler and F# Interactive (Linux only)](https://fsharp.org/use/linux/)
49 |
50 | ## Workshop Feedback
51 |
52 | > "Awesome F# workshop by @jorgefioranelli" [@Sahebjade tweet](https://twitter.com/Sahebjade/status/1132234942333636609)
53 |
54 | > "Outstanding weekend #fsharp workshop led by @jorgefioranelli at @Jet's Hoboken HQ. #functionalprogramming #learning" [@JetTechnology tweet](https://twitter.com/JetTechnology/status/931957480258752513)
55 |
56 | > "Thanks @jorgefioranelli for the wonderful workshop " [@ckumareddy tweet](https://twitter.com/ckumareddy/status/931944429237231623)
57 |
58 | > "Great🆒workshop🔛 #code @Jet with @jorgefioranelli in functional #programming @fsharporg all #software #developers & #businessnews #businessdevelopment should ✔out" [@tomsnode tweet](https://twitter.com/tomsnode/status/931983517067640833)
59 |
60 | > "Thanks @jorgefioranelli for the awesome workshop at the NYC #fsharp lab hours. And thanks @JetTechnology for sponsoring." [@pblasucci tweet](https://twitter.com/pblasucci/status/734139405301075969)
61 |
62 | > "Thanks to @jorgefioranelli for teaching and @Valtech for hosting an awesome F# workshop last Saturday in London, learned a lot." [@pedromsantos tweet](https://twitter.com/pedromsantos/status/716903753442586624)
63 |
64 | > "Thanks @jorgefioranelli and @Valtech for today's amazing F# workshop. See you soon!" [@vgaltes tweet](https://twitter.com/vgaltes/status/716339992860164096)
65 |
66 | > "@DCFSharp with @jorgefioranelli running an awesome #fsharp workshop!" [@trikace tweet](https://twitter.com/TRikace/status/708679222441725953)
67 |
68 | > "DC F# Meetup - @jorgefioranelli 's F# Workshop - excellent intro to F#. Thanks!" [@cnromaine tweet](https://twitter.com/cnromaine/status/709380386124656641)
69 |
70 | > "Thanks to @jorgefioranelli for teaching and @CoStarGroup for hosting a great F# workshop this weekend! Learning a bunch." [@sjkilleen tweet](https://twitter.com/sjkilleen/status/708716443660111873)
71 |
72 | > "Thanks @jorgefioranelli for awesome #fsharp workshop!" [@grishace tweet](https://twitter.com/grishace/status/696073536595718144)
73 |
74 | > "Fantastic F# workshop today by @jorgefioranelli and @liammclennan - really clear, interesting, and enjoyable!" [@harrietgl tweet](https://twitter.com/harrietgl/status/600872399538532352)
75 |
76 | > "A truly excellent #F#Workshop yesterday by @jorgefioranelli . Thank you!" [@marcote_torres tweet](https://twitter.com/marcote_torres/status/598842148927201280)
77 |
78 | > "Check out @jorgefioranelli's #fsharp workshop, one of the best workshops I've attended! http://www.fsharpworkshop.com" [@fekberg tweet](https://twitter.com/fekberg/status/563477575230431234)
79 |
80 | > "Seriously impressed at the quality and professionalism of the #readifyfsharpworkshop by @jorgefioranelli and @tobycmoore" [@robdmoore tweet](https://twitter.com/robdmoore/status/532522727941218304)
81 |
82 | > "Can't praise @jorgefioranelli enough for the amazing outstanding preparation & delivery of his @readify #fsharp workshop tonight" [@meligy tweet](https://twitter.com/Meligy/status/514382840520138753)
83 |
84 | ## Recommended Learning Material
85 |
86 | Video: [Functional Programming: What? Why? When?](https://vimeo.com/97514630) by Robert C. Martin
87 |
88 | Video: [5 Reasons to Move from C# to F#](https://www.youtube.com/embed/-0BB3lU_qr4) by Jorge Fioranelli
89 |
90 | Practice Online: [dotnetfiddle](https://dotnetfiddle.net/)
91 |
92 | Book: [Real-World Functional Programming](http://www.manning.com/petricek/) by Tomas Petricek
93 |
94 | ## License
95 |
96 | Licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
97 |
98 | Copyright 2018 Jorge Fioranelli
99 |
--------------------------------------------------------------------------------