Is pattern matching on derived types idiomatic for F #?

I want to implement the following along the most idiomatic path

The player is on the map.

  • If he is in the same position with the arrow, he takes 1 damage

  • If he is in the same position with the creature, he takes damage equal to the creature's hp

  • If he is in the same position with the coin, he gets $ 1

  • If he is in the same position as the medicine, he heals for 1

Here is a stub written for interaction:

open System
[<AbstractClass>]
type ActorBase(x,y,symbol)=
    member this.X:int=x
    member this.Y:int=y
    member this.Symbol:char=symbol

type Medication(x,y)=
    inherit ActorBase(x,y,'♥')
type Coin(x,y)=
    inherit ActorBase(x,y,'$') 

type Arrow(x,y,symbol,targetX,targetY) =
    inherit ActorBase(x,y,symbol)
    member this.TargetX=targetX
    member this.TargetY=targetY

[<AbstractClass>]
type CreatureBase(x,y,symbol,hp) =
    inherit ActorBase(x,y,symbol)
    member this.HP:int=hp

type Player(x,y,hp, score) =
    inherit CreatureBase(x,y,'@',hp)
    member this.Score = score
type Zombie(x,y,hp,targetX,targetY) =
    inherit CreatureBase(x,y,'z',hp)
    member this.TargetX=targetX
    member this.TargetY=targetY

let playerInteraction (player:Player) (otherActor:#ActorBase):unit =
    printfn "Interacting with %c" otherActor.Symbol
    match (otherActor :> ActorBase) with
            | :? CreatureBase as creature -> printfn "Player is hit by %d by creature %A" (creature.HP) creature
            | :? Arrow -> printfn "Player is hit by 1 by arrow" 
            | :? Coin -> printfn "Player got 1$" 
            | :? Medication -> printfn "Player is healed by 1"
            | _ -> printfn "Interaction is not recognized" 

let otherActorsWithSamePosition (actor:#ActorBase) =
    seq{
        yield new Zombie(0,0,3,1,1) :> ActorBase
        yield new Zombie(0,1,3,1,1) :> ActorBase
        yield new Arrow(0,0,'/',1,1) :> ActorBase
        yield new Coin(0,0) :> ActorBase
        yield new Medication(0,0) :> ActorBase
    } 
        |> Seq.where(fun a -> a.X=actor.X && a.Y=actor.Y)
[<EntryPoint>]
let main argv = 
    let player = new Player(0,0,15,0)
    for actor in (otherActorsWithSamePosition player) do
        playerInteraction player actor
    Console.ReadLine() |> ignore
    0

1) Are classes and inheritance used for use in F #? Or are they just compatible with .Net? Can I use records instead, and if so, how?

2) ​​#. F #? , otherActorsWithSamePosition? otherXsWithSamePosition X, ,

UPDATE:

, :

type IActor =
    abstract member X:int
    abstract member Y:int
    abstract member Symbol:char
type IDamagable =
    abstract member Damaged:int->unit
type IDamaging =
    abstract member Damage:int
type Player =
    {
        X:int
        Y:int
        HP:int
        Score:int
    }
    interface IActor with
        member this.X=this.X
        member this.Y=this.Y
        member this.Symbol='@'
    interface IDamagable with
        member this.Damaged damage = printfn "The player is damaged by %d" damage
    interface IDamaging with
        member this.Damage = this.HP
type Coin =
    {
        X:int
        Y:int
    }
    interface IActor with
        member this.X=this.X
        member this.Y=this.Y
        member this.Symbol='$'
type Medication =
    {
        X:int
        Y:int
    }
    interface IActor with
        member this.X=this.X
        member this.Y=this.Y
        member this.Symbol='♥'
type Arrow =
    {
        X:int
        Y:int
        DestinationX:int
        DestinationY:int
        Symbol:char
    }
    interface IActor with
        member this.X=this.X
        member this.Y=this.Y
        member this.Symbol=this.Symbol
    interface IDamaging with
        member this.Damage = 1
type Zombie =
    {
        X:int
        Y:int
        DestinationX:int
        DestinationY:int
        HP:int
    }
    interface IActor with
        member this.X=this.X
        member this.Y=this.Y
        member this.Symbol='z'
    interface IDamaging with
        member this.Damage = this.HP
type Actor =
    |Player of Player
    |Coin of Coin
    |Zombie of Zombie
    |Medication of Medication
    |Arrow of Arrow
let otherActorsWithSamePosition (actor:Actor) =
    seq{
        yield Zombie {X=0;Y=0; HP=3;DestinationX=1;DestinationY=1}
        yield Zombie {X=0;Y=1; HP=3;DestinationX=1;DestinationY=1}
        yield Arrow {X=0;Y=0; Symbol='/';DestinationX=1;DestinationY=1}
        yield Coin {X=0;Y=0}
        yield Medication {X=0;Y=0}
    } 
        //Cannot cast to interface
        |> Seq.where(fun a -> (a:>IActor).X=actor.X && (a:>IActor).Y=actor.Y)
let playerInteraction player (otherActor:Actor) =
    match otherActor with
            | Coin coin -> printfn "Player got 1$" 
            | Medication medication -> printfn "Player is healed by 1"
            //Cannot check this
            | :?IDamaging as damaging -> (player:>IDamagable).Damaged(damaging.Damage)

[<EntryPoint>]
let main argv = 
    let player = Player {X=0;Y=0;HP=15;Score=0}
    for actor in (otherActorsWithSamePosition player) do
        playerInteraction player actor
    Console.ReadLine() |> ignore
    0

:

1) :

Actor =
    | Medication {x:int;y:int;symbol:char} 

type Medication = {x:int;y:int;symbol:char}
Actor =
        | Medication

Medication Actor.Medication

type Medication = {x:int;y:int;symbol:char}
Actor =
    | Medication of Medication

.

2) F #. , 'member this.X = this.X'. - , "IActor", .

F #? ?

+4
2

F #?

"", , F #.

F # . . , F # ML OCaml, OO. , OO, . OO, , , OO.

F #? .Net?

MSDN F # classes When to Use Classes, Unions, Records, and Structures

, , . - . - , , .NET Framework. F # .NET Framework - - , , , .

- , , , , - , . , , . . (F #).

.

, , , ?

, , , . , . it depends.

, , , , , . , , . , , , , , - . . (F #).

, , .NET. - .NET. , . , , . , , . , . . (F #).

.

​​#. F #?

. Is pattern matching on derived types idiomatic for F#?

?

namespace Game

type Location = int * int
type TargetLocation = Location
type CurrentLocation = Location
type Symbol = char
type ActorType = CurrentLocation * Symbol
type HitPoints = int
type Health = int
type Money = int
type Creature = ActorType * HitPoints

// Player = Creature * Health * Money
//        = (ActorType * HitPoints) * Health * Money
//        = ((CurrentLocation * Symbol) * HitPoints) * Health * Money
//        = ((Location * Symbol) * HitPoints) * Health * Money
//        = (((int * int) * char) * int) * int * int
type Player = Creature * Health * Money 

type Actor =
    | Medication of ActorType
    | Coin of ActorType
    | Arrow of Creature * TargetLocation    // Had to give arrow hit point damage
    | Zombie of Creature * TargetLocation

module main =

    [<EntryPoint>]
    let main argv = 

        let player = ((((0,0),'p'),15),0,0)  

        let actors : Actor List = 
            [
                 Medication((0,0),'♥'); 
                 Zombie((((3,2),'Z'),3),(0,0)); 
                 Zombie((((5,1),'Z'),3),(0,0)); 
                 Arrow((((4,3),'/'),3),(2,1));
                 Coin((4,2),'$'); 
            ]

        let updatePlayer player (actors : Actor list) : Player =
            let interact (((((x,y),symbol),hitPoints),health,money) : Player) otherActor = 
                match (x,y),otherActor with
                | (playerX,playerY),Zombie((((opponentX,opponentY),symbol),zombieHitPoints),targetLocation) when playerX = opponentX && playerY = opponentY -> 
                    printfn "Player is hit by creature for %i hit points." zombieHitPoints
                    ((((x,y),symbol),hitPoints - zombieHitPoints),health,money)
                | (playerX,playerY),Arrow((((opponentX,opponentY),symbol),arrowHitPoints),targetLocation)  when playerX = opponentX && playerY = opponentY ->  
                    printfn "Player is hit by arrow for %i hit points." arrowHitPoints
                    ((((x,y),symbol),hitPoints - arrowHitPoints),health,money)
                | (playerX,playerY),Coin((opponentX,opponentY),symbol)  when playerX = opponentX && playerY = opponentY ->  
                    printfn "Player got 1$." 
                    ((((x,y),symbol),hitPoints),health,money + 1)
                | (playerX,playerY),Medication((opponentX,opponentY),symbol)  when playerX = opponentX && playerY = opponentY ->  
                    printfn "Player is healed by 1."
                    ((((x,y),symbol),hitPoints),health+1,money)
                | _ ->  
                    // When we use guards in matching, i.e. when clause, F# requires a _ match 
                    ((((x,y),symbol),hitPoints),health,money) 
            let rec updatePlayerInner player actors =
                match actors with
                | actor::t ->
                    let player = interact player actor
                    updatePlayerInner player t
                | [] -> player
            updatePlayerInner player actors

        let rec play player actors =
            let player = updatePlayer player actors
            play player actors

        // Since this is example code the following line will cause a stack overflow.
        // I put it in as an example function to demonstrate how the code can be used.
        // play player actors

        // Test

        let testActors : Actor List = 
            [
                Zombie((((0,0),'Z'),3),(0,0))
                Arrow((((0,0),'/'),3),(2,1))
                Coin((0,0),'$')
                Medication((0,0),'♥')
            ]

        let updatedPlayer = updatePlayer player testActors

        printf "Press any key to exit: "
        System.Console.ReadKey() |> ignore
        printfn ""

        0 // return an integer exit code

, , .

Player is hit by creature for 3 hit points.
Player is hit by arrow for 3 hit points.
Player got 1$.
Player is healed by 1.

, , .

, OO , , , OO . , OO, OO .

+6

1) F #? .Net? , , ? , - .

  • OO () ().
  • FP () ( ).

, OO , ; FP , . . , . F # , ; FP F #, .

2) ​​#. F #? , otherActorsWithSamePosition? otherXsWithSamePosition X, ,

OO, F #. . FP . DU, , ( ) . , , OO.

.

  • , 1
  • , , hp
  • , 1 $
  • , 1

. , , , . , , , , . .

, F #:

type Player = 
    { Hp: int
      Score: int }
type Zombie =
    { Hp: int
      TargetLocation: (int*int) option }

type Creature =
| Zombie of Zombie

type Actor =
| Arrow
| Medication
| Creature of Creature
| Coin
| Player of Player

, , - , , , :

let symbol = function
| Arrow -> '/'
| Medication -> '♥'
| Creature c -> 
    match c with
    | Zombie _ -> 'X'
| Coin -> '$'
| Player _ -> '@'

, , Actor list [][], 2D- .

let width, height = 10, 10
let map = Array.init height (fun y -> Array.init width (fun x -> List.empty<Actor>))

// Let put some things in the world
map.[0].[1] <- [Arrow]
map.[2].[2] <- [Creature(Zombie { Hp = 10; TargetLocation = None })]
map.[0].[0] <- [Player { Hp = 20; Score = 0}]

, , , , .

playerInteraction ( , ):

let applyEffects { Hp = hp; Score = score } actor =
    let originalPlayer = { Hp = hp; Score = score }
    match actor with
    | Arrow -> { originalPlayer with Hp = hp - 1 }
    | Coin -> { originalPlayer with Score = score + 1 }
    | Medication -> { originalPlayer with Hp = hp + 1 }
    | Creature(Zombie z) -> { originalPlayer with Hp = hp - z.Hp }
    | _ -> originalPlayer

, , : ? . , . ( , 2D-, , ):

let getPlayer: Player * (int * int) =
    let mapIterator = 
        map 
        |> Seq.mapi(fun y row -> 
            row |> Seq.mapi(fun x actors -> actors, (x, y))) 
        |> Seq.collect id
    mapIterator 
    |> Seq.pick(fun (actors, (x, y)) -> 
        actors |> Seq.tryPick(function 
                              | Player p -> Some (p, (x, y)) 
                              | _ -> None))
+2

All Articles