I think this problem falls a bit into the domain of the Design by Contract concept. The idea of DbC is that you specify preconditions and postconditions on parameters. The callers then have to guarantee that these conditions will be met. Ideally, this guarantee is enforced by the compiler. I think this feature is more or less what you are trying to emulate with OO.

Coincidentally, are there any plans by the F# team to add DbC features to f#? I know there was research into this by one of the teams at MS - IIRC the tech was called SpecSharp..

By on 3/24/2010 9:24 AM ()

I always thought it would be nice to efficiently have a "value" slot that every type had - any use of an instance of the type would automatically call the Value. The default would be something like:

member this.$Value = this

which would essentially get compiled completely away - if it is anything else, then an indirection is made - inline as efficiently as possible. This let's you create all kinds of "thunking" types (or "swizzled pointers") that include lazy creation, persistence loading and cases like these "phantom types".

type ValidPathString = // pseudocode
val str : string
new(s) = if not isValid(str) raise "doh!" else str <- string
member this.$Value = str

Of course, now the value of the instance isn't the same as the type of the instance, so it probably requires some changes in many places...

In an object-table based system like the orig. Smalltalk you could also have an object become: another object, but seeing that's too expensive in a reference/pointer based system, we need to maintain the indirection even once we don't need it, unless we have self-modifying code to smash-out the indirection with a NOP - essentially allowing a one-time mutuation back to the "this" behavior.)

By on 3/23/2010 4:16 PM ()

You have at least a few options for doing something along these lines. However, one important note is that what you have defined is a type alias, which won't give you the safety you want - given your definition of validatedPath, people can pass plain strings wherever you're expecting a validatedPath.

One solution to your problem is to define a new type which does validation in a constructor:

1
2
3
type ValidatedPath = 
  val Path : string
  new(s:string) = { Path = if (valid s) then s else failwith "Invalid path" }

Another option would be to bring validation outside of the class, but to use a constructor with limited accessibility:

1
2
3
4
type ValidatedPath internal(path : string) =
  member this.Path = path

let makeValidPath s = if (valid s) then ValidatedPath s else failwith "Invalid path"

Now external code can't construct a value of type ValidatedPath directly (because the constructor isn't accessible), but must use the makeValidPath function instead.

By on 3/23/2010 3:20 PM ()

Sorry for taking so long to get back on this but the day job took over for a couple of months. I eventually went for a solution similar to kvb's first suggestion and it suits my purposes very well - thank you. However, I did hesitate because at first sight it did not seem like a purely functional solution as it uses a class. Thinking a little more carefully I concluded that even though a class is used, it is a functional solution as the state (i.e. val Path : string) is not mutated. Any thoughts on whether my conclusion is correct would be appreciated.

By on 5/27/2010 2:43 PM ()

I think this is a really hard problem to solve (I think about this a lot). The approach of a separate immutable wrapper type whose constructor does the validation is necessary to have the type system ensure the constraint. But it suffers in that existing APIs (e.g. System.IO.File.OpenRead()) won't use the type, and that all calls on the wrapped type need to go through an extra accessor (e.g. myValidatedPath.Value.Length) unless you duplicate e.g. the whole 'string' API on the 'ValidatedPath' type.

Some research languages might use 'phantom types' to manage this, I think, it has been a while since I looked at that. (F# units of measure can be almost-slightly-approximately be used like phantom types, but the feature is not a great match, and can easily be cast away, and does not interop with other languages.)

By on 3/23/2010 3:14 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