Fold function does not produce expected result - f#

The following fold function loads duplicate players into the bases record:
(initializedBase, plays) ||> List.fold (fun bases play ->
Some play.Player |> move bases)
The test is the following:
[<Test>]
let ``2 players each hit single``() =
// Setup
let players = [{ Player= Scott; Hit= Single }; { Player= Brian; Hit= Single }]
// Test
let bases = players |> assignBases
bases |> should equal { First=Some Brian; Second=Some Scott; Third=None }
The actual result is:
{ First=Some Scott; Second=Some Scott; Third=None }
I expected:
{ First=Some Brian; Second=Some Scott; Third=None }
The entire code is here:
module Foo
(*Types*)
type Position =
| First
| Second
| Third
type Player =
| Scott
| Brian
| Cherice
type Hit =
| Single
| Double
| Triple
type Play = { Player: Player; Hit: Hit }
type Bases = {
First:Player option
Second:Player option
Third:Player option
}
(*Functions*)
let assignBases (plays:Play list) =
let initializedBase = { First=None; Second=None; Third=None }
let move bases player =
match bases with
| { First= None; Second=None; Third=None } -> { bases with First=player }
| { First= player; Second=None; Third=None } -> { bases with First=player; Second=bases.First }
| { First= None; Second=player; Third=None } -> { bases with First=None; Second=None; Third=player }
| { First= None; Second=None; Third=player } -> { bases with First=player }
| _ -> initializedBase // fill the rest out later...
(initializedBase, plays) ||> List.fold (fun bases play ->
Some play.Player |> move bases)
(*Tests*)
open NUnit.Framework
open FsUnit
[<Test>]
let ``2 players each hit single``() =
// Setup
let players = [{ Player= Scott; Hit= Single }; { Player= Brian; Hit= Single }]
// Test
let bases = players |> assignBases
bases |> should equal { First=Some Brian; Second=Some Scott; Third=None }

It doesn't look like the problem has anything to do with Seq.fold as such. If I'm not mistaken, the error is in the second line of the match expression in the move function:
| { First=player; Second=None; Third=None } -> { bases with First=player; Second=bases.First }
The pattern uses player to capture the value of the First field of bases. Although it may seem like it should, this does not match that to the value of player from outside the match expression! If you needed that, you would need to use a when clause additionally.
As it is, from the moment F# determines that second case is the correct one (which will be Second and Third being None and First having a Some value), until control leaves the whole match expression, the name player denotes a new binding and is said to be "shadowing" the old player value.
Thus, when you assign player to First in the result part of that match case, that will again be the original value of bases.First - the same that you then set for Second.
So what you want that second match case to be is
| { First=oldFirst; Second=None; Third=None } -> { bases with First=player; Second=oldFirst }
Now, player still refers to the same value as outside the match expression, which is the one you want to enter the field at First, and Second gets the player you "picked up" from First.

Related

Traversing a rosetree to generate a new rosetree

I have a data type that is very close to a rosetree
type RoseTree<'T> =
{
Root: 'T
Children: LazyList<RoseTree<'T>>
}
I want to take instances of this tree and map it to a new tree but where the calculation in a specific node is dependent on values in other nodes, a simple example would be take a RoseTree and "count" the nodes in some traversal and map it to a RoseTree
so
()
-> ()
->()
-> ()
->()
->()
and get
0
-> 1
-> 2
-> 3
-> 4
-> 5
I can hand write out a specific recursive function to do this, but I was hoping i could use some off the shelf functions to do this sort of thing simply by passing a function that accumulates the 'count'.
(the actual traversal path probably doesnt massively matter)
The usual suspects don't seem to fit though.
map : ('a->'b) -> RoseTree<'a> -> RoseTree<'b>
this maps isolated nodes, with no reference to other nodes.
If this were a List then fold would work, an accumator being folded through each element could generate a new List whilst counting, but folding a tree doesnt seem to work like this (this is the Haskell data-tree implementation)
foldTree :: (a -> [b] -> b) -> Tree a -> b
this seems to process each child by mapping the subtrees over the fold in isolation to each other, there is no accumulator that gets passed around each node in turn.
If we take unfold
unfold : ('a -> 'b * LazyList<'a>) -> 'a -> RoseTree<'a>
then the seed would be something like
int * RoseTree<unit>
but it seems that the unfolder function generates each subtree effectively in isolation to every other subtree by generating a list of new seeds.
(this is quite surprising, I felt that 'unfold' was the goto function to generate any tree).
So out of the standard set of funtional patterns/typeclasses, is there one that can be used for this sort of traveral + accumulator type processing?
(I do know about Zippers, but they seem to be overkill for this sort of general desire to process each node in turn and accumulate.
I can handwrite something that does specifically what i want, something like:
let foldish : ('accum -> 'a -> 'accum * 'b) -> RoseTree<'a> -> 'accum -> RoseTree<'b> =
maybe doing something like a depth first search and traversing the Tree and recording the path I take, then use list.fold to label the nodes with numbers and then unfold a new tree from it, seems a bit mechanical and i'd rather use 'standard' idioms/patterns if one exists).
If you want the output tree to have the same shape as the input tree, this sounds a lot like mapFold:
let rec mapFold (mapping : 'State -> 'T -> 'Result * 'State) (state : 'State) (tree : RoseTree<'T>) : RoseTree<'Result> * 'State =
let result, state' = mapping state tree.Root
let children, state'' =
List.mapFold (mapFold mapping) state' tree.Children
let tree' =
{
Root = result
Children = children
}
tree', state''
Note that I made the children non-lazy for simplicity:
type RoseTree<'T> =
{
Root: 'T
Children: List<RoseTree<'T>>
}
Test case:
let tree =
{
Root = ()
Children =
[
{
Root = ()
Children =
[ { Root = (); Children = [] } ]
}
{
Root = ()
Children =
[
{ Root = (); Children = [] }
{ Root = (); Children = [] }
]
}
]
}
(0, tree)
||> mapFold (fun count _ ->
count, count + 1)
|> fst
|> printfn "%A"
Output:
{ Root = 0
Children =
[{ Root = 1
Children = [{ Root = 2
Children = [] }] };
{ Root = 3
Children = [{ Root = 4
Children = [] }; { Root = 5
Children = [] }] }] }

Deleting a node from singly linked list has the error "cannot move out of borrowed content"

I am making a singly-linked list. When you delete a node, the previous node's next should become the current node's next (prev->next = curr->next;) and return data if the index matches. Otherwise, the previous node becomes the current node and the current node becomes the next node (prev = curr; curr = curr->next;):
struct Node<T> {
data: T,
next: Option<Box<Node<T>>>,
}
struct LinkedList<T> {
head: Option<Box<Node<T>>>,
}
impl LinkedList<i64> {
fn remove(&mut self, index: usize) -> i64 {
if self.len() == 0 {
panic!("LinkedList is empty!");
}
if index >= self.len() {
panic!("Index out of range: {}", index);
}
let mut count = 0;
let mut head = &self.head;
let mut prev: Option<Box<Node<i64>>> = None;
loop {
match head {
None => {
panic!("LinkedList is empty!");
}
Some(c) => {
// I have borrowed here
if count == index {
match prev {
Some(ref p) => {
p.next = c.next;
// ^ cannot move out of borrowed content
}
_ => continue,
}
return c.data;
} else {
count += 1;
head = &c.next;
prev = Some(*c);
// ^^ cannot move out of borrowed content
}
}
}
}
}
fn len(&self) -> usize {
unimplemented!()
}
}
fn main() {}
error[E0594]: cannot assign to field `p.next` of immutable binding
--> src/main.rs:31:33
|
30 | Some(ref p) => {
| ----- consider changing this to `ref mut p`
31 | p.next = c.next;
| ^^^^^^^^^^^^^^^ cannot mutably borrow field of immutable binding
error[E0507]: cannot move out of borrowed content
--> src/main.rs:31:42
|
31 | p.next = c.next;
| ^ cannot move out of borrowed content
error[E0507]: cannot move out of borrowed content
--> src/main.rs:40:37
|
40 | prev = Some(*c);
| ^^ cannot move out of borrowed content
Playground Link for more info.
How can I do this? Is my approach wrong?
Before you start, go read Learning Rust With Entirely Too Many Linked Lists. People think that linked lists are easy because they've been taught them in languages that either don't care if you introduce memory unsafety or completely take away that agency from the programmer.
Rust does neither, which means that you have to think about things you might never have thought of before.
There are a number of issues with your code. The one that you ask about, "cannot move out of borrowed content" is already well-covered by numerous other questions, so there's no reason to restate all those good answers:
Cannot move out of borrowed content
Cannot move out of borrowed content when trying to transfer ownership
error[E0507]: Cannot move out of borrowed content
TL;DR: You are attempting to move ownership of next from out of a reference; you cannot.
p.next = c.next;
You are attempting to modify an immutable reference:
let mut head = &self.head;
You allow for people to remove one past the end, which doesn't make sense to me:
if index >= self.len()
You iterate the entire tree not once, but twice before iterating it again to perform the removal:
if self.len() == 0
if index >= self.len()
All of that pales in comparison to the fact that your algorithm is flawed in the eyes of Rust because you attempt to introduce mutable aliasing. If your code were able to compile, you'd have a mutable reference to previous as well as a mutable reference to current. However, you can get a mutable reference to current from previous. This would allow you to break Rust's memory safety guarantees!
Instead, you can only keep track of current and, when the right index is found, break it apart and move the pieces:
fn remove(&mut self, index: usize) -> T {
self.remove_x(index)
.unwrap_or_else(|| panic!("index {} out of range", index))
}
fn remove_x(&mut self, mut index: usize) -> Option<T> {
let mut head = &mut self.head;
while index > 0 {
head = match { head }.as_mut() {
Some(n) => &mut n.next,
None => return None,
};
index -= 1;
}
match head.take().map(|x| *x) {
Some(Node { data, next }) => {
*head = next;
Some(data)
}
None => None,
}
}
See also:
Cannot obtain a mutable reference when iterating a recursive structure: cannot borrow as mutable more than once at a time
How do I get an owned value out of a `Box`?
Playground Link for more info.
There are numerous problems with the rest of your code, such as the fact that the result of your insert method is unlike any I've ever seen before.
How I'd write it.

F# Types and Function signatures

I'm new to F# and I'm trying a few thing to get my head around the language.
I have to two types almost identical (Coordinate and Vector). Because of this type inference cannot work properly and I have hard time to specify the correct type on each function.
It somehow undestand that's a Vector here:
type Coordinate = {X:int; Y:int}
type Vector = {X:int; Y:int}
let calculateVector (origin:Coordinate) (destination:Coordinate) = { X=destination.X-origin.X; Y= destination.Y-origin.Y;}
And here when I want a return type of Coordinate, I cannot find how to specify the return it for this function:
let calculateNextCoordinate (coordinate:Coordinate) direction =
match direction with
| "N" -> { X=coordinate.X; Y=coordinate.Y-1 }
| "NE" -> { X=coordinate.X+1; Y=coordinate.Y-1 }
| "E" -> { X=coordinate.X+1; Y=coordinate.Y }
| "SE" -> { X=coordinate.X+1; Y=coordinate.Y+1 }
| "S" -> { X=coordinate.X; Y=coordinate.Y+1 }
| "SW" -> { X=coordinate.X-1; Y=coordinate.Y+1 }
| "W" -> { X=coordinate.X-1; Y=coordinate.Y }
| "NW" -> { X=coordinate.X-1; Y=coordinate.Y-1 }
| _ -> coordinate
I have this error on the default case: This expression was expected to have 'Vector' but here has type 'Coordinate'
i tired to have a look on this website for function signatures but could not something for my problem: https://fsharpforfunandprofit.com/posts/function-signatures/
Questions:
How do you fix this error?
Is it because inference type take by default the last type declared that match the properties (in my example Vector)?
Bonus: Is there a better way to handle this kind of situation in F#?
Thanks in advance
Since records are constructed using their member names:
{ X = 2; Y = 3}
you created a naming conflict between Vector and Coordinate. In F# the compiler always resolves to the latest definition in such cases and therefore, in your example, the compiler will interpret the record { X = ..., Y = ...} as a Vector.
There is a good article on F# records on fsharpforfunandprofit.com, that explains how to handle this easily, which I suggest you read for a good explanation.
But in short, you can prefix either member of the record instance with the record type:
{ Coordinate.X = 2; Y = 3 } // Creates a Coordinate
{ X = 2; Coordinate.Y = 3 } // Creates a Coordinate
and
{ Vector.X = 2; Y = 3 } // creates a vector

How to move from external function to functional lens

On my journey to start getting better with functional programming, I discovered, with the help of a member of the SO family, what lens. I even made some research on it with the links down below to understand more about them.
https://www.schoolofhaskell.com/school/to-infinity-and-beyond/pick-of-the-week/basic-lensing
http://fluffynukeit.com/how-functional-programming-lenses-work/
https://medium.com/#dtipson/functional-lenses-d1aba9e52254#.27yw4gnwk
With all that knowledge, I thought I could give them a try and see whether or not I could understand their functionnality and the reasons why they're useful in FP. My problem at the moment is moving from the type members that were define to access and modify fields in my equipment record that I've define for a game that I'm prototyping at the moment. I will put snippets of the Equipment records, the members that were there before and the functional lens I'm trying to create but just won't work. After the first pattern matching, it expects the code to have the same return value, when I'd like it to be a general value to be returned, depending on a pattern that I had successfully matched!
For the the remain of the code, instead of omitting the code and making it not compile while you're trying to give me a hand, I've thought it be best to put the important snippets here and a public to the relevant code so you can compile it on your local machine ! The public gist can be found here. It's a lot of for my definitions, the relevant code is from line 916.
type Equipment = {
Helmet : Hat option
Armor : Armor option
Legs : Pants option
Gloves : Gauntlets option
Ring : Ring option
Weapon : Weaponry option
Shield : Shield option
Loot : ConsumableItem option
}
let equipPurchasedProtection newItem (inventory,equipment) =
match newItem with
| Helmet ->
match equipment.Helmet with
| None ->
let newEquipment = { equipment with Helmet = Some newItem }
(inventory,newEquipment)
| Some oldHelm
if (playerWantsToAutoEquip newItem) then
let newEquipment = { equipment with Helmet = Some newItem }
let newInventory = inventory |> addToInventory oldHelm
(newInventory,newEquipment)
else
let newInventory = inventory |> addToInventory newItem
(newInventory,equipment)
| Gloves ->
match equipment.Hands with
| None ->
let newEquipment = { equipment with Hands = Some newItem }
(inventory,newEquipment)
| Some oldGloves
if (playerWantsToAutoEquip newItem) then
let newEquipment = { equipment with Hands = Some newItem }
let newInventory = inventory |> addToInventory oldGloves
(newInventory,newEquipment)
else
let newInventory = inventory |> addToInventory newItem
(newInventory,equipment)
| Boots ->
match equipment.Feet with
| None ->
let newEquipment = { equipment with Boot = Some newItem }
(inventory,newEquipment)
| Some oldBoots
if (playerWantsToAutoEquip newItem) then
let newEquipment = { equipment with Boot = Some newItem }
let newInventory = inventory |> addToInventory oldBoots
(newInventory,newEquipment)
else
let newInventory = inventory |> addToInventory newItem
(newInventory,equipment)
let equipPurchasedItem newItem (inventory,equipment) =
let equipFunction =
match newItem with
| Protection(Helmet(_)) -> genericEquipFunction HelmetFun_
| Protection(Gloves(_)) -> genericEquipFunction GlovesFun_
| Protection(Legs(_)) -> genericEquipFunction LegsFun_
| Protection(Armor(_)) -> genericEquipFunction ArmorFun_
| Protection(Ring(_)) -> genericEquipFunction RingFun_
| Protection(Shield(_)) -> genericEquipFunction ShieldFun_
| Weapon _ -> genericEquipFunction WeaponFun_
| Consumable HealthPotion -> genericEquipFunction LootFun_
| Consumable HighHealthPotion -> genericEquipFunction LootFun_
| Consumable MegaHealthPotion -> genericEquipFunction LootFun_
| Consumable Elixir -> genericEquipFunction LootFun_
| Consumable HighElixir -> genericEquipFunction LootFun_
| Consumable MegaElixir -> genericEquipFunction LootFun_
| Consumable PhoenixFeather -> genericEquipFunction LootFun_
| Consumable MedicinalHerb -> genericEquipFunction LootFun_
let itemForInventory,newEquipment = equipFunction (Some newItem) equipment
match itemForInventory with
| None -> (inventory,newEquipment)
| Some item ->
let newInventory = inventory |> addToInventory { Item = item; Count = 1 }
(newInventory,newEquipment)
UPDATE 1
Here's a look at one of the lens function that I'm using to equip purchased items.
let getArmorFun e = e.Armor
let equipArmorFun newArmor e = { e with Armor = newArmor }
let ArmorFun_ = (getArmorFun, equipArmorFun)
Having looked at your model more closely, I can confirm my initial impression: you're using a lot more types than you should. Many of those types should be instances; in this case, record instances. Here's a good rule-of-thumb for when you should use a type or an instance. If the two things are interchangeable, they should be two instances of the same type. If they're NOT interchangeable, then (and only then) they should be two different types. Here's an example of what I mean. Here's a section of your code that takes up an entire screen:
type Weaponry =
| Dagger of Dagger
| Sword of Sword
| Axe of Axe
| Spear of Spear
| Staff of Staff
| LongBlade of Blade
| Spellbook of Spellbook
with
member x.Name =
match x with
| Dagger d -> d.ToString()
| Sword s -> s.ToString()
| Axe a -> a.ToString()
| Spear s -> s.ToString()
| Staff s -> s.ToString()
| LongBlade lb -> lb.ToString()
| Spellbook sb -> sb.ToString()
member x.Price =
match x with
| Dagger w -> w.Price
| Sword w -> w.Price
| Axe w -> w.Price
| Spear w -> w.Price
| Staff w -> w.Price
| LongBlade w -> w.Price
| Spellbook w -> w.Price
member x.Weight =
match x with
| Dagger w -> w.Weight
| Sword w -> w.Weight
| Axe w -> w.Weight
| Spear w -> w.Weight
| Staff w -> w.Weight
| LongBlade w -> w.Weight
| Spellbook w -> w.Weight
member x.Stats =
match x with
| Dagger w -> w.WeaponStats :> IStats
| Sword w -> w.WeaponStats :> IStats
| Axe w -> w.WeaponStats :> IStats
| Spear w -> w.WeaponStats :> IStats
| Staff w -> w.WeaponStats :> IStats
| LongBlade w -> w.WeaponStats :> IStats
| Spellbook w -> w.SpellStats :> IStats
What's different between all these items? The last line, where Spellbooks have SpellbookStats instead of WeaponStats. That's it! As for your other weapon types -- dagger, sword, axe, spear, etc... they're ALL identical in "shape". They all have weapon stats, price, weight, etc.
Here's a redesign of that entire weapon model:
type ItemDetails = { Weight: float<kg>; Price: int<usd> }
type PhysicalWeaponType =
| Dagger
| Sword
| Axe
| Spear
| Staff
| LongBlade
type MagicalWeaponType =
| Spellbook
// Could later add wands, amulets, etc.
type WeaponDetails =
| PhysicalWeapon of PhysicalWeaponType * WeaponStat
| MagicalWeapon of MagicalWeaponType * SpellbookStats
type Weaponry =
{ Name: string
ItemDetails: ItemDetails
WeaponDetails: WeaponDetails }
with member x.Weight = x.ItemDetails.Weight
member x.Price = x.ItemDetails.Price
member x.Stats = match x.WeaponDetails with
| PhysicalWeapon (_, stats) -> stats :> IStats
| MagicalWeapon (_, stats) -> stats :> IStats
// Now let's create some weapons. In the real game this would be read
// from a JSON file or something, so that the game is easily moddable
// by end users who want to add their own custom weapons.
let rustedDagger = {
Name = "Rusted dagger"
ItemDetails = { Weight = 2.10<kg>; Price = 80<usd> }
WeaponDetails = PhysicalWeapon (Dagger, { Damage = 5.60<dmg>; Defense = 1.20<def>; Intelligence = None; Speed = 1.00<spd>; Critical = 0.02<ctr>; HitLimit = 20<hl>; Rank = RankE })
}
let ironDagger = {
Name = "Iron dagger"
ItemDetails = { Weight = 2.80<kg>; Price = 200<usd> }
WeaponDetails = PhysicalWeapon (Dagger, { Damage = 9.80<dmg>; Defense = 2.30<def>; Intelligence = None; Speed = 1.10<spd>; Critical = 0.04<ctr>; HitLimit = 25<hl>; Rank = RankD })
}
let steelDagger = {
Name = "Steel dagger"
ItemDetails = { Weight = 4.25<kg>; Price = 350<usd> }
WeaponDetails = PhysicalWeapon (Dagger, { Damage = 13.10<dmg>; Defense = 3.00<def>; Intelligence = None; Speed = 1.15<spd>; Critical = 0.05<ctr>; HitLimit = 30<hl>; Rank = RankC })
}
let brokenSword = {
Name = "Broken sword"
ItemDetails = { Weight = 7.20<kg>; Price = 90<usd> }
WeaponDetails = PhysicalWeapon (Sword, { Damage = 5.40<dmg>; Defense = 2.50<def>; Intelligence = None; Speed = 1.20<spd>; Critical = 0.01<ctr>; HitLimit = 10<hl>; Rank = RankE })
}
let rustedSword = {
Name = "Rusted sword"
ItemDetails = { Weight = 8.50<kg>; Price = 120<usd> }
WeaponDetails = PhysicalWeapon (Sword, { Damage = 8.75<dmg>; Defense = 2.90<def>; Intelligence = None; Speed = 1.05<spd>; Critical = 0.03<ctr>; HitLimit = 20<hl>; Rank = RankD })
}
// And so on for iron and steel swords, plus all your axes, spears, staves and long blades.
// They should all be instances, not types. And spellbooks, too:
let rank1SpellbookDetails = { Weight = 0.05<kg>; Price = 150<usd> }
let rank2SpellbookDetails = { Weight = 0.05<kg>; Price = 350<usd> }
let bookOfFireball = {
Name = "Fireball"
ItemDetails = rank1SpellbookDetails
WeaponDetails = MagicalWeapon (Spellbook, { Damage = 8.0<dmg>; AttackRange = 1; Rank = RankE; Uses = 30 ; ManaCost = 12.0<mp> })
}
// Same for Thunder and Frost
let bookOfHellfire = {
Name = "Hellfire"
ItemDetails = rank2SpellbookDetails
WeaponDetails = MagicalWeapon (Spellbook, { Damage = 6.50<dmg>; AttackRange = 2; Rank = RankD; Uses = 25; ManaCost = 20.0<mp> })
}
// And so on for Black Fire and Storm of Blades
let computeCharacterOverallOffensive
// (rank: WeaponRank) // Don't need this parameter now
(weapon: Weaponry)
(cStats: CharacterStats) =
let weaponDamage =
match weapon.WeaponDetails with
| PhysicalWeapon (_, stats) -> stats.Damage
| MagicalWeapon (_, stats) -> stats.Damage
let weaponRank =
match weapon.WeaponDetails with
| PhysicalWeapon (_, stats) -> stats.Rank
| MagicalWeapon (_, stats) -> stats.Rank
// This should really be a method on the Rank type
let rankMultiplier =
match weaponRank with
| RankE -> 1.0100
| RankD -> 1.0375
| RankC -> 1.0925
| RankB -> 1.1250
| RankA -> 1.1785
| RankS -> 1.2105
cStats.Strength * rankMultiplier * weaponDamage
Notice how all the details of the Weaponry type fit on one screen now? And there's WAY less duplication. I kept the distinction between different types of physical weapons (daggers, swords, etc) since it's likely that you'll have characters that specialize in one or two types: a sword specialist can't use an axe, or he takes a 50% strength penalty when he uses an axe, and so on. But I doubt that you're ever going to have a character who can only use iron daggers but can't use steel daggers. Different types of daggers are completely interchangeable in this kind of game -- the player would be VERY surprised if they weren't. So they shouldn't be different types. And the various types of physical weapons are almost interchangeable, so their models should be as similar as possible, too. Put the stats in the part that doesn't differ, and leave the type (Dagger, Sword, Axe) as the only difference between the physical weapons.
This has been a really long answer and I still haven't gotten into your actual question about lenses! But since I winced on looking at the code and thought, "He is making WAY too much work for himself", I had to address this part first.
I think you'd benefit from taking your code over to https://codereview.stackexchange.com/ and asking people there to take a look at it and suggest ways to tighten up your model. Once your model is improved, I think you'll find the lens code to be a lot easier to write as well. And as I said before, DON'T try to write the lens code on your own! Use a library like Aether or F#+ to help you. In your shoes, I'd probably go with Aether simply because it has more documentation than F#+ seems to have; F#+ seems (AFAICT) to be more aimed at people who have already used Haskell lenses and don't need any reminders about how to use them.
UPDATE 1: Have another snippet for how I'd suggest you do armor:
type CharacterProtectionStats = {
Defense : float<def>
Resistance : float<res>
Intelligence : float<intel> option
MagicResist : float<mgres>
Speed : float<spd>
EquipmentUsage : int<eu>
}
with
interface IStats with
member x.showStat() =
sprintf "Defense : %O - Resistance : %O - Magic resistance : %O - Speed : %O - Equipment usage : %O" x.Defense x.Resistance x.MagicResist x.Speed x.EquipmentUsage
type CharacterProtectionDetails = {
Name : string
// No Type field here, because that's staying in the DU
ItemDetails : ItemDetails
ArmorStats : CharacterProtectionStats
}
type Hat = Hat of CharacterProtectionDetails
type Armor = Armor of CharacterProtectionDetails
type Pants = Pants of CharacterProtectionDetails
// etc.
type CharacterProtection =
| Shield of Shield
// | Ring of Ring // REMOVED. Rings are different; see below.
| Gloves of Gauntlets
| Legs of Pants
| Armor of Armor
| Helmet of Hat
let sorcererHat = Hat {
Name = "Sorcerer Hat"
ItemDetails = { Weight = 1.0<kg>; Price = 120<usd> }
ArmorStats = { Defense = 1.20<def>; Resistance = 1.30<res>; Intelligence = Some 3.00<intel>; MagicResist = 1.80<mgres>; Speed = 1.00<spd>; EquipmentUsage = 100<eu> }
}
// Other hats...
let steelArmor = Armor.Armor {
Name = "Steel Armor"
ItemDetails = { Weight = 15.0<kg>; Price = 450<usd> }
ArmorStats = { Defense = 17.40<def>; Resistance = 6.10<res>; Intelligence = None; MagicResist = 2.30<mgres>; Speed = 0.945<spd>; EquipmentUsage = 100<eu> }
}
// "Armor.Armor" is kind of ugly, but otherwise it thinks "Armor" is
// CharacterProtection.Armor. If we renamed the CharacterProtection DU
// item to ChestProtection instead, that could help.
type AccessoryStats = {
ExtraStrength : float<str> option
ExtraDamage : float<dmg> option
ExtraHealth : float<hp> option
ExtraMana : float<mp> option
}
with
interface IStats with
member x.showStat() =
sprintf ""
static member Initial =
{ ExtraDamage = None; ExtraStrength = None; ExtraHealth = None; ExtraMana = None }
type Ring = {
Name : string
ItemDetails : ItemDetails
RingStats : AccessoryStats
}
type Amulet = {
Name : string
ItemDetails : ItemDetails
AmuletStats : AccessoryStats
}
type AccessoryItems =
| Ring of Ring
| Amulet of Amulet
// Could add other categories too
let standardRingDetails = { Weight = 0.75<kg>; Price = 275<usd> }
let strengthRing = {
Name = "Extra strength ring"
ItemDetails = standardRingDetails
RingStats = { RingStats.Initial with ExtraStrength = Some 4.50<str> }
}
let damageRing = {
Name = "Extra damage ring"
ItemDetails = standardRingDetails
RingStats = { RingStats.Initial with ExtraDamage = Some 5.00<dmg> }
}

Eliminating my explicit state passing via like, monads and stuff

I'm working through the book Land of Lisp in F# (yeah weird, I know). For their first example text adventure, they make use of global variable mutation and I'd like to avoid it. My monad-fu is weak, so right now I'm doing ugly state passing like this:
let pickUp player thing (objects: Map<Location, Thing list>) =
let objs = objects.[player.Location]
let attempt = objs |> List.partition (fun o -> o.Name = thing)
match attempt with
| [], _ -> "You cannot get that.", player, objs
| thing :: _, things ->
let player' = { player with Objects = thing :: player.Objects }
let msg = sprintf "You are now carrying %s %s" thing.Article thing.Name
msg, player', things
let player = { Location = Room; Objects = [] }
let objects =
[Room, [{ Name = "whiskey"; Article = "some" }; { Name = "bucket"; Article = "a" }];
Garden, [{ Name = "chain"; Article = "a length of" }]]
|> Map.ofList
let msg, p', o' = pickUp player "bucket" objects
// etc.
How can I factor out the explicit state to make it prettier? (Assume I have access to a State monad type if it helps; I know there is sample code for it in F# out there.)
If you want to use the state monad to thread the player's inventory and world state through the pickUp function, here's one approach:
type State<'s,'a> = State of ('s -> 'a * 's)
type StateBuilder<'s>() =
member x.Return v : State<'s,_> = State(fun s -> v,s)
member x.Bind(State v, f) : State<'s,_> =
State(fun s ->
let (a,s) = v s
let (State v') = f a
v' s)
let withState<'s> = StateBuilder<'s>()
let getState = State(fun s -> s,s)
let putState v = State(fun _ -> (),v)
let runState (State f) init = f init
type Location = Room | Garden
type Thing = { Name : string; Article : string }
type Player = { Location : Location; Objects : Thing list }
let pickUp thing =
withState {
let! (player, objects:Map<_,_>) = getState
let objs = objects.[player.Location]
let attempt = objs |> List.partition (fun o -> o.Name = thing)
match attempt with
| [], _ ->
return "You cannot get that."
| thing :: _, things ->
let player' = { player with Objects = thing :: player.Objects }
let objects' = objects.Add(player.Location, things)
let msg = sprintf "You are now carrying %s %s" thing.Article thing.Name
do! putState (player', objects')
return msg
}
let player = { Location = Room; Objects = [] }
let objects =
[Room, [{ Name = "whiskey"; Article = "some" }; { Name = "bucket"; Article = "a" }]
Garden, [{ Name = "chain"; Article = "a length of" }]]
|> Map.ofList
let (msg, (player', objects')) =
(player, objects)
|> runState (pickUp "bucket")
If you want to use mutable state in F#, then the best way is just to write a mutable object. You can declare a mutable Player type like this:
type Player(initial:Location, objects:ResizeArray<Thing>) =
let mutable location = initial
member x.AddThing(obj) =
objects.Add(obj)
member x.Location
with get() = location
and set(v) = location <- v
Using monads to hide mutable state isn't as common in F#. Using monads gives you essentially the same imperative programming model. It hides the passing of state, but it doesn't change the programming model - there is some mutable state that makes it impossible to parallelize the program.
If the example uses mutation, then it is probably because it was designed in an imperative way. You can, change the program architecture to make it more functional. For example, instead of picking the item (and modifying the player), the pickUp function could just return some object representing a request to pick the item. The world would then have some engine that evaluates these requests (collected from all players) and calculates the new state of the world.

Resources