I have this F# class
module File1
open System
open System.Collections.Generic
type TimeRangeList<'e>(getter: DateTime * DateTime -> List<'e>, ?maybe_tFrom: DateTime, ?maybe_tTo: DateTime) as this =
inherit List<'e>()
//inherit List<'e>(getter(defaultArg maybe_tTo DateTime.Now, defaultArg maybe_tFrom ((defaultArg maybe_tTo DateTime.Now).AddDays(-1.0))))
let tTo = defaultArg maybe_tTo DateTime.Now
let tFrom = defaultArg maybe_tFrom (tTo.AddDays(-1.0))
do this.AddRange(getter(tFrom, tTo))
now I want to add constructors and use the syntax as in here
type TimeRangeList<'e> =
inherit List<'e>
val tFrom: DateTime
val tTo: DateTime
new (getter: DateTime * DateTime -> List<'e>, ?maybe_tFrom: DateTime, ?maybe_tTo: DateTime) = {
inherit List<'e>()
//inherit List<'e>(defaultArg maybe_tFrom ((defaultArg maybe_tTo DateTime.Now).AddDays(-1.0)), getter(defaultArg maybe_tTo DateTime.Now))
tTo = defaultArg maybe_tTo DateTime.Now
tFrom = defaultArg maybe_tFrom (tTo.AddDays(-1.0)) //tTo undefined
//tFrom = defaultArg maybe_tFrom ((defaultArg maybe_tTo DateTime.Now).AddDays(-1.0))
}
do this.AddRange(getter(tFrom, tTo)) //primary constructor required
this code gives two errors:
- in 'tFrom=...' it says 'tTo not defined' while tTo is clearly in scope; as a workaround I can repeat the defaultArg call as shown in the following (commented) line. Is there a better way?
- in the last line where 'AddRange' is called, it complains that do calls can be executed only in primary constructors, which is fair. But, how do I call the necessary AddRange to initialize the list? I have tried different options but couldn't find the way. A workaround is shown in the commented inherit line, but in the end I am calling defaultArg repeatedly and redundantly; there must be a clearer and more elegant way
2 Answers 2
This is the syntax you're looking for:
module File1
open System
open System.Collections.Generic
type TimeRangeList<'e> =
inherit List<'e>
val tFrom: DateTime
val tTo: DateTime
new (getter: DateTime * DateTime -> List<'e>, ?maybe_tFrom: DateTime, ?maybe_tTo: DateTime) as this =
let to_ = defaultArg maybe_tTo DateTime.Now
let from_ = defaultArg maybe_tFrom (to_.AddDays(-1.0))
{
inherit List<'e>()
tTo = to_
tFrom = from_
}
then
this.AddRange(getter(this.tFrom, this.tTo))
Documentation links:
- In additional constructors, use the
then
keyword instead ofdo
- To define the self-referential name for the instance in an additional constructor, put
as this
after thenew()
expression
To explain a little bit, the { field = value; field2 = value2 }
syntax doesn't have to be the only expression found in the new()
block that defines a secondary constructor. It just has to be the last expression, that is, the expression that is returned. (Here, even though technically the then
block is the "last" block in the constructor, its return value (which is required to be unit
) is ignored and the actual return value of the constructor is the last expression not found in a then
block). Therefore, it's safe to use let
expressions earlier to define the values you want to put into your class's fields, and those let
expressions can reference each other just as they would in normal code. So if you have a complicated or expensive calculation that you need to put into several fields, you could so something like:
new () =
let result = expensiveCalculationIWantToDoOnlyOnce()
{ field1 = result; field2 = result + 1; field3 = result + 2 }
-
Thanks for the precious information. A bit more details in the official documentation wouldn't hurt though, imhoFranco Tiveron– Franco Tiveron2018年06月30日 00:37:18 +00:00Commented Jun 30, 2018 at 0:37
-
@FrancoTiveron - Agreed, the documentation could be better. If you scroll down to the bottom of the documentation, though, you can submit a PR through Github to improve the docs. Yes, Microsoft has gone open-source with their documentation! So now, when you discover something that's poorly documented, you at least have the ability to do something about it. You may not have the time (I don't at the moment), but I'm happy that the option to improve the docs is actually available.rmunn– rmunn2018年06月30日 07:51:03 +00:00Commented Jun 30, 2018 at 7:51
To fix the first problem you should give a name to the current object: new (...) as this =
and then access your variable with it this.tTo.AddDays(-1.0)
.
I don't have a solution yet for the second issue.
type TimeRangeList<'e>() =
. This won't fix your code but you will have more meaningful errors.