How to get John1 string value from this object?
type Pilot2 = (string * int * string * int)
let p1 = Pilot2("John1", 1901, "Laren_TM1", 1)
let name: string = p1.[1] <<<<<- this errors
This is retarded..but seems to work.
type Pilot2 = (string * int * string * int)
let p1 = Pilot2("John1", 1901, "Laren_TM1", 1)
let (_, b: int, _, _) = p1
System.Console.WriteLine(b)
You could write this:
type Pilot2 = (string * int * string * int)
let p1 = Pilot2("John1", 1901, "Laren_TM1", 1)
p1 |> fun (s, _, _, _) -> s |> System.Console.WriteLine
Or define a helper function
type Pilot2 = (string * int * string * int)
let p1 = Pilot2("John1", 1901, "Laren_TM1", 1)
let nameOfPilot = fun (s, _, _, _) -> s
System.Console.WriteLine (nameOfPilot p1)
Though most would use a record type instead of a tuple here:
type Pilot2 = {
Name : string
Year : int
Foo : string
Bar : int
}
let p1 = {Name = "John1"; Year = 1901; Foo = "Laren_TM1"; Bar = 1}
System.Console.WriteLine p1.Name
Related
I am a newbie to F#. In WPF, I am using DisplayMemberBinding within a Datagrid as:
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:AppointmentListView ItemsSource="{Binding Columns[0].AppointmentKeys}" Height="140" Background="Bisque">
<ListView.View>
<GridView>
<GridViewColumn Header="First" DisplayMemberBinding="{Binding FirstName}" Width="100"/>
<GridViewColumn Header="Last" DisplayMemberBinding="{Binding LastName}" Width="120"/>
<GridViewColumn Header="BirthDate" DisplayMemberBinding="{Binding BirthDate, StringFormat=d}" Width="100"/>
</GridView>
</ListView.View>
</local:AppointmentListView>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
The (complete) backing F# module (in Elmish.wpf) is:
module MyDataGrid.DataGrid
open Elmish
open Elmish.WPF
open System
type Visit =
{ ServiceTime: DateTime option
DoNotSee: Boolean option
ChartNumber: int option
LastName: string option
FirstName: string option
Mi: string option
BirthDate: DateTime option
PostingTime: DateTime option
AppointmentTime: DateTime option }
type Cell =
{RowNumber: int
ColumnNumber: int
AppointmentKeys: Visit list
ColumnTime: TimeSpan
AppointmentCount: int
AppointmentTime: DateTime option // all lines in the cell have the same appointment time.
}
let SetCell (rowNumber: int, columnNumber: int) =
let AppointmentsPerCell = 4
{RowNumber = rowNumber
ColumnNumber = columnNumber
AppointmentKeys = [for x in 1 .. AppointmentsPerCell ->
{
ServiceTime = Some System.DateTime.Now
DoNotSee = Some false
ChartNumber = Some 8812
LastName= Some ("LastName" + string x)
FirstName= Some ("FirstName" + string x)
Mi = Some "J"
BirthDate = Some(DateTime(2020,09,14))
PostingTime = Some DateTime.Now
AppointmentTime = Some DateTime.Now
}]
ColumnTime = System.TimeSpan.FromMinutes(float(columnNumber * 15))
AppointmentCount = 4
AppointmentTime = Some(DateTime.Now)
}
type Row =
{RowTime: string
Columns: Cell list}
let SetRow (rowNumber: int, startTime: System.TimeSpan)=
let columnCount = 4
let hr = System.TimeSpan.FromHours(1.0)
let rowTime = startTime + System.TimeSpan.FromTicks(hr.Ticks * int64(rowNumber))
{ RowTime = rowTime.ToString("h':00'")
Columns = [for columnNumber in 1 .. columnCount -> SetCell(rowNumber, columnNumber) ]
}
type Model =
{ AppointmentDate: DateTime
Rows: Row list
SelectedRow: Row option}
type Msg =
| SetAppointmentDate of DateTime
| SetSelectedRow of Row option
let init =
let rowCount = 9
let startTime = TimeSpan.FromHours(float(8))
{ AppointmentDate = DateTime.Now
Rows = [for rowNumber in 0 .. rowCount -> SetRow(rowNumber, startTime)]
SelectedRow = None
}
let update msg m =
match msg with
| SetAppointmentDate d -> {m with AppointmentDate = d}
| SetSelectedRow r -> {m with SelectedRow = r}
let bindings () : Binding<Model, Msg> list = [
"SelectedAppointmentDate" |> Binding.twoWay( (fun m -> m.AppointmentDate), SetAppointmentDate)
"Rows" |> Binding.oneWay( fun m -> m.Rows)
"SelectedRow" |> Binding.twoWay( (fun m -> m.SelectedRow), SetSelectedRow)
]
let designVm = ViewModel.designInstance init (bindings ())
let main window =
Program.mkSimpleWpf (fun () -> init) update bindings
|> Program.withConsoleTrace
|> Program.runWindowWithConfig
{ ElmConfig.Default with LogConsole = true; Measure = true }
window
The DisplayMememberBindings show LastName as "Some(LastName1)" and BirthDate as "Some(09/14/2020 00:00:00)".
How can I get the LastName: string option to return either null or the value of the string so the display shows "LastName1" and not "Some(LastName1)?
The same goes for the birth date, how to show BirthDate as "9/14/2020" and not "Some(09/14/2020 00:00:00)?
TIA
Full source code at: Example DataGrid
Your code only has three bindings. You should have a binding for every individual piece of data. Specifically, you should change your Rows binding from a OneWay binding to a SubModel binding. Then repeat this for all your other types.
Then, the question you specifically asked about is how to display LastName1 instead of Some(LastName1) and 9/14/2020 instead of Some(09/14/2020 00:00:00). Create the bindings for these individual pieces of optional data with Binding methods that ends in Opt like Binding.oneWayOpt or Binding.twoWayOpt.
For newbies like me, here is my full F# working solution in Elmish.WPF/F#:
module MyDataGrid.DataGrid
open Elmish
open Elmish.WPF
open System
module Visit =
type Model =
{ ServiceTime: DateTime option
DoNotSee: Boolean option
ChartNumber: int option
LastName: string option
FirstName: string option
Mi: string option
BirthDate: DateTime option
PostingTime: DateTime option
AppointmentTime: DateTime option
Id: int}
let SetVisits appointmentsPerCell = [for x in 1 .. appointmentsPerCell ->
{
ServiceTime = Some System.DateTime.Now
DoNotSee = Some false
ChartNumber = Some 8812
LastName= Some ("LastName" + string x)
FirstName= Some ("FirstName" + string x)
Mi = Some "J"
BirthDate = Some(DateTime(2020,09,14))
PostingTime = Some DateTime.Now
AppointmentTime = Some DateTime.Now
Id = x
}]
let bindings() = [
"FirstName" |> Binding.oneWayOpt( fun (_, m) -> m.FirstName)
"LastName" |> Binding.oneWayOpt( fun (_, m) -> m.LastName)
"BirthDate" |> Binding.oneWayOpt( fun (_, m) -> m.BirthDate)
"ServiceTime" |> Binding.oneWayOpt( fun (_, m) -> m.ServiceTime)
]
module Cell =
type Model =
{ RowNumber: int
ColumnNumber: int
AppointmentKeys: Visit.Model list
ColumnTime: TimeSpan
AppointmentCount: int
AppointmentTime: DateTime option // all lines in the cell have the same appointment time.
Id: int
}
let SetCell (rowNumber: int, columnNumber: int) =
let AppointmentsPerCell = 4
{RowNumber = rowNumber
ColumnNumber = columnNumber
AppointmentKeys = Visit.SetVisits AppointmentsPerCell
ColumnTime = System.TimeSpan.FromMinutes(float(columnNumber * 15))
AppointmentCount = 4
AppointmentTime = Some(DateTime.Now)
Id=rowNumber*10 + columnNumber
}
let bindings() =[
"AppointmentKeys" |> Binding.subModelSeq(
(fun (_, m) -> m.AppointmentKeys),
(fun v -> v.Id),
Visit.bindings
)
]
module Row =
type Model =
{ RowTime: string
Columns: Cell.Model list
Id: int }
let SetRow (rowNumber: int, startTime: System.TimeSpan)=
let columnCount = 4
let hr = System.TimeSpan.FromHours(1.0)
let rowTime = startTime + System.TimeSpan.FromTicks(hr.Ticks * int64(rowNumber))
{ RowTime = rowTime.ToString("h':00'")
Columns = [for columnNumber in 1 .. columnCount -> Cell.SetCell(rowNumber, columnNumber) ]
Id = rowNumber
}
let bindings () = [
"RowTime" |> Binding.oneWay( fun (_,r) -> r.RowTime)
"Columns" |> Binding.subModelSeq(
(fun (_, m) -> m.Columns),
(fun c -> c.Id),
Cell.bindings
)
]
type Model =
{ AppointmentDate: DateTime
Rows: Row.Model list
SelectedRow: Row.Model option}
type Msg =
| SetAppointmentDate of DateTime
| SetSelectedRow of Row.Model option
let init () =
let rowCount = 9
let startTime = TimeSpan.FromHours(float(8))
{ AppointmentDate = DateTime.Now
Rows = [for rowNumber in 0 .. rowCount -> Row.SetRow(rowNumber, startTime)]
SelectedRow = None
}
let update msg m =
match msg with
| SetAppointmentDate d -> {m with AppointmentDate = d}
| SetSelectedRow r -> {m with SelectedRow = r}
let bindings () : Binding<Model, Msg> list = [
"SelectedAppointmentDate" |> Binding.twoWay( (fun m -> m.AppointmentDate), SetAppointmentDate)
"Rows" |> Binding.subModelSeq(
(fun m -> m.Rows),
(fun r -> r.Id),
Row.bindings
)
"SelectedRow" |> Binding.twoWay( (fun m -> m.SelectedRow), SetSelectedRow)
]
let main window =
Program.mkSimpleWpf init update bindings
|> Program.withConsoleTrace
|> Program.runWindowWithConfig
{ ElmConfig.Default with LogConsole = true; Measure = true }
window
enter code here
I want to create a label in F# which uses a mutable variable to return a value. Unfortunately F# sets this label to a constant value. If the value of the mutable changes, the value of the label remains. Isn't it a bit inconsistent? Is there a way to get the label ("a") being dependent of the mutable ("x")?
let mutable x = 0;
let a = x + 2; // I want not to set a to a constant value
let b two = x + two;
x <- 1;
let c = b 2;
let isConsistent = a = c;
val mutable x : int = 1
val a : int = 2
val b : two:int -> int
val c : int = 3
val isConsistent : bool = false
From your own comment you want a to be a function returning x + 2
Direct translation of that is :
let mutable x = 0
let a () = x + 2
let b two = x + two
x <- 1
let c = b 2
let isConsistent = a () = c // don't forget to call the function 'a'
(*
val mutable x : int = 1
val a : unit -> int
val b : two:int -> int
val c : int = 3
val isConsistent : bool = true
*)
is it possible to have the same operator in a type definition work with different types on the right-hand side of the operator?
Definition of the |== operator via name plugElts compiles fine, but using it later in module RemoverTest fails with error FS0002: This function takes too many arguments, or is used in a context where a function is not expected when function is supplied on the right-hand side.
module SampleOperators =
let inline (|==) (x: ^URel) (wires: ^Wires) =
(^URel: (static member plugElts: ^URel * ^Wires -> 'R) (x, wires))
module Remover =
open SampleOperators
type RemGroup<'G> = RemGroupPass | RemGroupAll | RemGroupExcept of 'G | RemGroupElts of 'G
type RemMap<'K,'P when 'K: comparison> =
RemGroup<Map<'K,'P>>
type RemFun<'K,'P when 'K: comparison> =
'K * 'P -> bool
type Rem<'K,'MapVal,'FunVal when 'K:comparison> =
| Map_ of RemMap<'K,'MapVal>
| Fun_ of RemFun<'K,'FunVal>
type X<'K,'P when 'K:comparison> =
{ id: 'K
vv: Rem<'K,'P,'P> }
static member inline plugElts (x:X<_,_>, ws:('K * 'P) seq) =
{x with vv = Map_ (RemGroupElts (Map.ofSeq ws))}
static member inline plugElts (x:X<_,_>, i:int) =
{x with vv = Map_ (RemGroupElts (Map.ofSeq [i,i]))}
static member inline plugElts (x:X<_,_>, fn:('K * 'P -> bool)) =
{x with vv = Fun_ fn}
module RemoverTest =
open Remover
open SampleOperators
let xxx1 () =
{id = 1; vv = Map_ RemGroupPass} |== [1,1] // compiles ok
let xxx2 () =
{id = 1; vv = Map_ RemGroupPass} |== 1 // compiles ok
let xxx3 () =
({id = 1; vv = Map_ RemGroupPass}:X<_,_>) |== (fun _ -> bool) // can't compile
Are there ways to make this work without wrapping the right-hand side in discriminated union?
Thanks,
Karol
edit: added overload for right-hand side with int type which works fine
The moment you invoke the operator in xxx3 you need to provide an actual function - what you have there right now is only the equivalent of a type declaration. Change to the following, and it will compile:
let xxx3 () =
({id = 1; vv = Map_ RemGroupPass}:X<_,_>) |== (fun _ -> true)
Here's a more compact version of your question:
type MoreFun<'T> = 'T -> int
type X<'T> =
{
B: int
F: MoreFun<'T>
}
static member (|==) (a: X<_>, b: int) = { B = b; F = fun (f:int) -> b}
static member (|==) (a: X<_>, b: MoreFun<_>) = { a with F = b }
module Tests =
let one (f: int) = 1
let t1() = { B = 1; F = one } |== 2
// let t2() = { B = 1; F = one } |== (fun _ -> int) // Does not work
let three: MoreFun<_> = (fun _ -> 3)
let t3() = { B = 1; F = one } |== three
// You don't need to cast three to be of type MoreFun:
let t4() = { B = 1; F = one } |== (fun _ -> 3)
I'm trying to return a list from a function, but I'm getting an error that says that an unit was expected instead. Also, I would like to know if this code appears to be structured correctly in general.
code:
let rec calculateVariants (attList: NewProductAttributeInfo list) (activeCount: int)
(currentList: (int * NewProductAttributeInfo) list) =
// group attribute list by category id
let attGrouped = attList |> List.groupBy (fun x -> x.AttributeCategoryId)
// define mutable list
let mutable stageList = currentList
// begin iteration
for catId,details in attGrouped do
for d in details do
if activeCount = 0
then stageList <- (activeCount,d) :: stageList
let groupLength = attGrouped.Length
if (activeCount + 1) <= groupLength
then
let selectCat,selectDetails = attGrouped.[activeCount + 1]
selectDetails
|> List.filter (fun x ->
stageList
|> List.exists (fun (x') ->
not(x' = (activeCount,x))))
|> (fun x ->
match x with
| [] -> ()
| head :: tail ->
stageList <- (activeCount, head) :: stageList
let currentCategory = activeCount + 1
calculateVariants attList currentCategory stageList
)
stageList // <-- error Unit expected
if .. then .. else should return the same type on both branches. If you omit else branch then compiler assuming that it returns unit. Add else branch returning list.
Edit:
Given your problem description, the easiest way would be something like this:
type NewProductAttributeInfo = {AttributeCategoryId: string; AttributeId: string}
let products = [ { AttributeCategoryId = "Size"; AttributeId = "S"};
{ AttributeCategoryId = "Mat"; AttributeId = "Linen" };
{ AttributeCategoryId = "Mat"; AttributeId = "Poliester" };
{ AttributeCategoryId = "Color"; AttributeId = "White" };
{ AttributeCategoryId = "Color"; AttributeId = "Green" };
{ AttributeCategoryId = "Mat"; AttributeId = "Linen" };
{ AttributeCategoryId = "Mat"; AttributeId = "Cotton" };
{ AttributeCategoryId = "Mat"; AttributeId = "Poliester" };
{ AttributeCategoryId = "Size"; AttributeId = "XL" } ]
let group list =
list
|> Set.ofList // Provides uniqueness of attribute combinations
|> Seq.groupBy (fun x -> x.AttributeCategoryId) // Grouping by CatId
|> List.ofSeq
let res = group products
Result:
val it : (string * seq<NewProductAttributeInfo>) list =
[("Color", seq [{AttributeCategoryId = "Color";
AttributeId = "Green";}; {AttributeCategoryId = "Color";
AttributeId "White";}]);
("Mat",
seq
[{AttributeCategoryId = "Mat";
AttributeId = "Cotton";}; {AttributeCategoryId = "Mat";
AttributeId = "Linen";};
{AttributeCategoryId = "Mat";
AttributeId = "Poliester";}]);
("Size", seq [{AttributeCategoryId = "Size";
AttributeId = "S";}; {AttributeCategoryId = "Size";
AttributeId = "XL";}])]
This is the solution that I came with. It works, but I'm sure it can be optimized quite a bit. I have a duplicate issue that is solved with the Set.ofList function externally after this code runs, which I'm still working on.
type NewProductAttributeInfo = {
AttributeId : string;
AttributeCategoryId : string
}
let rec private returnVariant (curIdx: int) (listLength: int)
(attList: (int * NewProductAttributeInfo * NewProductAttributeInfo) list)
(curList: NewProductAttributeInfo list) =
match curList with
| x when x.Length = listLength -> curList
| x ->
let attTup =
attList
|> List.filter (fun x' ->
let idx1,att1,att2' = x'
idx1 >= curIdx && not(curList
|> List.exists (fun x'' ->
x'' = att2'))
)
let idx1,att1,att2 = attTup |> List.head
let newList = curList # [att2]
returnVariant idx1 newList.Length attList newList
let rec calculateVariants (attList: NewProductAttributeInfo list)
(currentList: (int * NewProductAttributeInfo * NewProductAttributeInfo) list) =
// group attribute list by category id
let attGrouped = attList |> List.groupBy (fun x -> x.AttributeCategoryId)
let (firstGroupCatId,firstGroupDetails) = attGrouped.[0]
match currentList with
| [] ->
let rawVariants = [for nxt in 0 .. (attGrouped.Length - 1) do
if nxt > 0
then
// begin iteration
for d in firstGroupDetails do
let _,det = attGrouped.[nxt]
for det' in det do
yield (nxt, d, det')
]
calculateVariants attList rawVariants
| x ->
let groupLength = x |> List.groupBy (fun (idx,d0,nxtD) -> idx)
|> List.length |> ((+)1)
let sortedGroup = x |> List.sortBy (fun (x,y,z) -> x)
if groupLength > 2
then // below is the block that generates the duplicates
[for att in sortedGroup do
for attCompare in sortedGroup do
let idx1,att1,att2 = att
let idx2,attC1,attC2 = attCompare
if idx2 > idx1 && att2 <> attC2
then
let idString =
returnVariant idx2 groupLength x [att1; att2; attC2]
|> List.map (fun nl -> nl.AttributeId)
yield String.concat "," idString
]
else
[
for att in sortedGroup do
let idx1,att1,att2 = att
let idString =
returnVariant idx1 groupLength x [att1; att2]
|> List.map (fun nl -> nl.AttributeId)
yield String.concat "," idString
]
in F#, How to make a function wrapper (x:'T) to wrap any inputs to an array of array, say: wrapper(1) = [|[|1|]|]; wrapper([|1|]) = [|[|1|]|]; and wrapper([|[|1|]|]) = [|[|1|]|]; something like below does not work:
let wrapper (x:'T) =
let y =
if not <| x.GetType().IsArray then [|[|x|]|]
elif not <| x.[0].GetType().IsArray then [|x|]
else x
y
The following seems to work:
let wrapper<'a, 'b> (x:'a) =
match box x with
| null -> null
| :? array<array<'b>> as y -> y
| :? array<'b> as y -> [|y|]
| y -> [|[|unbox y|]|]
The signature is 'a -> array<array<'b>>.
In response to your comment: this sort of thing can be done in statically-typed languages--and, arguably better than in a dynamic language--but/because stepping outside the type system must be explicit (e.g., box/unbox).
Here's a reflection based solution which will accept inputs of arbitrary nested array depth:
open System.Text.RegularExpressions
let wrapper input =
let ty = input.GetType()
if ty.IsArray |> not then
[|[|input |> unbox|]|]
else
let depth = Regex.Matches(ty.Name, "\[\]", RegexOptions.Compiled).Count
let rec findInnerItem curDepth curArray =
let innerItem = curArray.GetType().GetMethod("Get").Invoke(curArray, [|box 0|])
if curDepth = depth then
innerItem
else
findInnerItem (curDepth+1) innerItem
let innerItem = findInnerItem 1 input
[|[|innerItem |> unbox|]|]
Usage in FSI:
val wrapper : 'a -> 'b [] []
> let x : int[][] = wrapper 1;;
val x : int [] [] = [|[|1|]|]
> let x : int[][] = wrapper [|1|];;
val x : int [] [] = [|[|1|]|]
> let x : int[][] = wrapper [|[|1|]|];;
val x : int [] [] = [|[|1|]|]
> let x : int[][] = wrapper [|[|[|1|]|]|];;
val x : int [] [] = [|[|1|]|]
> let x : int[][] = wrapper [|[|[|[|1|]|]|]|];;
val x : int [] [] = [|[|1|]|]
> let x : int[][] = wrapper [|[|[|[|[|1|]|]|]|]|];;
val x : int [] [] = [|[|1|]|]
> let x : int[][] = wrapper [|[|[|[|[|[|1|]|]|]|]|]|];;
val x : int [] [] = [|[|1|]|]
You can't, that's not a well-typed function (try to write out the type signature).
You function needs to "wrap any inputs to an array of array". As par this statement the solution is as simple as:
let wrapper (x:'T) = [|[|x|]|];
But then the examples you have given are not as par your function definition.
i.e wrapper([|1|]) = [|[|1|]|] should be wrapper([|1|]) = [|[|[|1|]|]|] as par your function definition.