Hi,
I'll try to give you more an answer that shows a more fundamental change that you can do when writing code in the functional style. I'm not sure what's some more concrete problem that you're concerned with, so I'll use a general example. However, it should answer your question about easy modificiation of nested data structures in functional programming.

In functional programming, you'll often define some data structure and then implement processing functions for working with it. For example, let's use a simple expressions:

1
2
3
4
5
6
 
type Expr =
 | Add of Expr * Expr
 | Num of int

let e = Add(Num(10), Add(Num(100), Num(1)))

The value 'e' is an expression and one of its deeply nested values is the value 'Num(100)'. Once we have the data structre, we can write a higher order function that allows us to modify any nested exprssions:

1
2
3
4
5
6
7
8
 
let rec modifyNestedNums f e = 
  match e with 
  | Add(a, b) -> 
      let a = modifyNestedNums f a
      let b = modifyNestedNums f b
      Add(a, b) 
  | Num(n) -> f(n)

This allows you to specify function that changes values of "Num" nodes in the data structure. Now we can write the following code to change only the "Num(100)" node without affecting anything else:

1
2
3
 
> e |> modifyNestedNums (fun n -> if n > 50 then Num(n/2) else Num(n)) ;;
val it : Expr = Add (Num 10,Add (Num 50,Num 1))

So, my point is that functional design gives a different answer than object oriented style in situations like this. It of course depends on the particular problem you're solving. In F#, you can choose between those two styles, so both of them make sometimes sense, however I think the functional way makes the code more readable once you're used to it.

By on 12/21/2008 5:01 PM ()

Kenny55: I would like to avoid the use of mutable data structures.

tomasp: My problem involves modeling cars and their four wheels. It must be possible to distinguish between e.g. the left front wheel, the right front wheel...
My modeling, which I think is typical of OO, makes heavy use of "the", as in "the left front wheel of car C1".
The example you give, using expressions, ends in a different category of genericity, I would say. As such, it would be possible to solve my problem the way you describe, but I fear it may end up being excessively generic. Thanks anyway for suggesting a new approach to my problem, I will think more about it.

By on 12/22/2008 2:56 AM ()

Replying to myself, here is some pseudo-code that's decent, I think, but still not quite good enough:

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
(* Examples of types *)

type Vector3 = Vector3 of double []
    static member X =
        Array.init 3 (fun i -> if (i = 0) then 1.0 else 0.0) |> Vector3
    static member Add((Vector3 u), (Vector3 v)) =
        Array.zip u v |> Array.map (fun (x, y) -> x + y) |> Vector3


type WheelPart =
    { model_name : string;
      object_name : string;
      transform : Matrix4;
      center : Vector3 }


type VehicleParts =
    { front_left_wheel : WheelPart;
      front_right_wheel : WheelPart; }
    static member Empty = ...


(* Functions to emulate the "obj -> subobj -> method (arg)" notation in C++ *)

let id x = x

let top vehicle =
    id, vehicle
     
let selWheelFR (newVehicle, vehicle) =
    (fun new_wheel -> newVehicle { vehicle with front_right_wheel = new_wheel }), vehicle.front_right_wheel

let selWheelCenter (newWheel, wheel : WheelPart) =
    (fun new_center -> newWheel { wheel with center = new_center }), wheel.center
    
let (-->) (update, v) f =
    update (f v)
                    
let vehicle = VehicleParts.Empty

let vehicle2 = (top vehicle |> selWheelFR) --> (fun w -> { w with model_name = "name"})
let vehicle3 = (top vehicle2 |> selWheelFR |> selWheelCenter) --> (fun c -> Vector3.Add(c, Vector3.X))
let vehicle4 = (top vehicle3 |> selWheelFR |> selWheelCenter) --> (fun _ -> Vector3.Zero) 

Silly question: is there a way to specify the respective priorities of operators? I don't like the parentheses before -->

Smarter question: I'm lazy and I would like a convenient (ideally fully automatic) way to define the sel* functions. Is that possible?

By on 12/22/2008 11:00 AM ()
1
2
3
4
5
6
7
8
type Number =

     val mutable num : int

     member this.Incr() = this.num /- this.num + 1

     new() = {num = 0}

does this solve it?

(Notice that / should be replaced with lesser than sign, but for some reason the same sign removes the rest of the line)

By on 12/21/2008 11:20 AM ()
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