Hi George,

Your code looks OK but be careful with the lower cases. I just tried to make some changes below without changing its structure.

#light

type callData = {

time : System.DateTime;

duration : int;

countryCode : string;

areaCode : string;

number : string

}

type callingPlan =

| Unlimited

| HundedMinutes

| PayAsYouGo

let isNightTime (hourOfDay:int) = hourOfDay < 5 or hourOfDay > 19

let isInternational (c:callData) = not (c.countryCode="")

let perMinuteRate (cd:callData, cp:callingPlan ) =

match cd with

//Rule1: 1-800 numbers are free

| cd when cd.areaCode="800" -> 0

//Rule2: customers on 'Unlimited' plans don't pay per-minute charges

| cd when (cp=Unlimited) -> 0

//Rule3: International calls cost 20c/daytime and 10c/nighttime

| cd when ( isInternational(cd) && isNightTime(cd.time.Hour) ) -> 10 // ##ERROR HERE

| cd when isInternational(cd) -> 20

//Rule 4: anything else costs 1 dollar per minute

| _ -> 100

By on 4/18/2007 2:53 PM ()

Here's myattempt using active patterns. Note the feature isn't primarily meant as a business rule language, since there are many good rule engines out there that use bespoke languages (and indeed some friends of mine work on one). But if you're going to write your rules in a general purpose language then this kind of device does look useful.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
 #light

type callData = 
  { time : System.DateTime;
    duration : int;
    countryCode : string;
    areaCode : string;
    number : string }

// Note: you ask for calling plans to be extensible. I've done this by using strings: in many ways
// active patterns allow you to use more heterogeneous data (such as strings) and still use
// functional programming (or at least pattern matching).
//
// The intention of the example appears to be that additional calling plans are given semantics
// by new rules that resolve how they interact in a fairly adhoc way with the call data.
// It's hard to see how you would permit "arbitrary" extensions in a modular way for
// this example, since the devil is always in the resolution of potential conflicts and
// ambiguities with other rules. If I've misunderstood the kind of extensibility you require
// then please let me know. In any case it's a great example of adhoc matching.
type userData = 
  { callingPlan : string }

// Define patterns that extract features out of the above data structures. Once
// these are defined we never access the internal data structures. 
//
//   Note for language geeks: we'll probably also add some kind of syntax for 
//   automatically getting active patterns that extract properties, which would make
//   these redundant.

let (|CallTime|)   cd = cd.time
let (|CountryCode|) cd = cd.countryCode
let (|AreaCode|)    cd = cd.areaCode
let (|CallingPlan|) ud = ud.callingPlan

// Define some patterns that detect particular features relevant to the rules.
let predicate b = if b then Some() else None

let (|NightTime|_|) (time:System.DateTime) = 
    let hourOfDay = time.TimeOfDay.Hours in 
    predicate (hourOfDay < 5 or hourOfDay > 19)

let (|International|_|) cc = 
    predicate (cc <> "")

let perMinuteRate (cd,ud) = 
    match cd,ud with
    //Rule1: 1-800 numbers are free
    | AreaCode("800"), _ -> 0
    //Rule2: customers on 'Unlimited' plans don't pay per-minute charges
    | _,CallingPlan("Unlimited") -> 0
    //Rule3: International calls cost 20c/daytime and 10c/nighttime
    | CountryCode(International) & CallTime(NightTime), _ -> 10 
    | CountryCode(International), _ -> 20
    //Rule 4: anything else costs 1 dollar per minute
    | _ -> 100 

By on 4/19/2007 3:51 PM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper