Appropriate use of active templates in F #

I am using active template to parse usage events in csv format usage log. The active part of the template is shown below. Parsing the entire file works well and the generated sequence is filled with all kinds of UsageEvents.

type SystemName = string
type SystemVersion = string
type MAC = string
type Category = string
type Game = string
type Setting = string
type StartupLocation = string

type UsageEvent =
    | SystemStart of DateTime * SystemVersion * SystemName * MAC
    | SystemEnd of DateTime
    | GameStart of DateTime * Category * Game * Setting * StartupLocation
    | GameEnd of DateTime * Category * Game
    | Other

let (|SystemStart|SystemEnd|GameStart|GameEnd|Other|) (input : string list) =
    match List.nth input 0 with
    | "SystemStartedEvent" ->
         SystemStart (DateTime.Parse (List.nth input 1), List.nth input 2, List.nth input 3, List.nth input 4)
    | "SystemEndedEvent" ->
         SystemEnd (DateTime.Parse (List.nth input 1))
    | "GameStartedEvent" ->
         GameStart (DateTime.Parse (List.nth input 1), List.nth input 2, List.nth input 3, List.nth input 4, List.nth input 5)
    | "GameEndedEvent" ->
         GameEnd (DateTime.Parse (List.nth input 1), List.nth input 2, List.nth input 3)
    | _ ->
         Other

      

The problem is that I am probably using ActivePattern incorrectly. I would like to traverse the list in order to create a tree from it based on some logic, but I have no way of matching the entry in the sequence after parsing.

let CountSystemStart (entries : UsageEvent list) =
    let rec loop sum = function
        | SystemStart(_,_,_,_) -> sum + 1
        | _ -> sum
    loop 0 entries

      

This match doesn't work because the loop function requires string list

. What other way can I use the data contained in the unions, or should I match the input and then store it in a normal type?

+3


source to share


2 answers


To add to @Petr's answers - UsageEvent

and your templates with active templates have the same names, so the active template that is defined later is a shadow union type. What comes from string list

is likely.

I would just completely remove the active template and add the function Parse

(or rather ParseParts

, since you want to pass it a list of strings) in UsageEvent

.



type UsageEvent =
    | SystemStart of DateTime * SystemVersion * SystemName * MAC
    | (...and so on...)
    static member ParseParts (input: string list) =
        match input with
        | ["SystemStartedEvent"; date; version; name; mac] ->
            SystemStart (DateTime.Parse date, version, name, mac)
        | (...and so on...) 

      

Active templates are cute, but you really need a good script to make them shine. Otherwise, if you can avoid using a simple function, just use a regular function.

+6


source


There are 2 problems in this code:

  • Discriminatory union UsageEvent

    and active pattern selection functions have the same names

  • A recursive loop function is not recursive because it is written - it does not call itself.

When you map in the UsageEvent list, try using the fully qualified type name.



I would rewrite your CountSystemStart function as:

let CountSystemStart (entries : UsageEvent list) =
    let rec loop sum = function
        | [] -> sum 
        | (UsageEvent.SystemStart(_))::rest -> loop (sum + 1) rest
        | _::rest -> loop sum rest
    loop 0 entries  

      

+2


source







All Articles