How add setter to to discriminated unions in F# - properties

I want add setter property to discriminated unions, how I should to do it?
f.e.:
type Factor =
| Value of Object
| Range of String
let mutable myProperty = 123
member this.MyProperty
with get() = myProperty
and set(value) = myProperty <- value

Here's how I might approach it:
type Value = { value: obj; mutable MyProperty: int }
type Range = { range: string; mutable MyProperty: int }
type Factor =
| Value of Value
| Range of Range
member this.MyProperty
with get() =
match this with
| Value { MyProperty=myProperty }
| Range { MyProperty=myProperty } -> myProperty
and set(myProperty) =
match this with
| Value x -> x.MyProperty <- myProperty
| Range x -> x.MyProperty <- myProperty
and use it like so:
let v = Value {value="hi":>obj ; MyProperty=0 }
v.MyProperty <- 2
match v with
| Value { value=value } as record ->
printfn "Value of value=%A with MyProperty=%i" value record.MyProperty
| _ ->
printfn "etc."
I've used this technique in a similar scenario to yours with happy results in FsEye's watch model: http://code.google.com/p/fseye/source/browse/tags/2.0.0-beta1/FsEye/WatchModel.fs.

Why not use a class and an active pattern:
type _Factor =
| Value_ of obj
| Range_ of string
type Factor(arg:_Factor) =
let mutable myProperty = 123
member this._DU = arg
member this.MyProperty
with get() = myProperty
and set(value) = myProperty <- value
let (|Value|Range|) (arg:Factor) =
match arg._DU with
|Value_(t) -> Value(t)
|Range_(t) -> Range(t)
This will obviously be significantly slower, but it allows you to do what you want

I'm not too familiar with F# yet, but I suppose you can't do this, it doesn't make any sense. Discriminated Unions as it can be seen from their name are unions. They represent some kind of a choice. And you're trying to incorporate some state into it. What're you trying to achieve? What's the use case?
Perhaps everything you need is to add additional "parameter" to your DU, i.e. if you have
type DU =
| A of int
| B of string
and you want to add setter of int type, then you can extend DU in such a way:
type DU =
| A of int * int
| B of string * int
member x.Set i =
match x with
| A(a1, a2) -> A(a1, i)
| B(b1, b2) -> B(b1, i)

Related

F# type constraints indexable

I'm trying to make a type that should represent a "slice" of some indexable collection.
I know that there are some similar types in F# but not one that specifies the criteria that I need.
To do this it needs to carry a reference to the collection of type 'content and the content needs to be indexable. So I tried this constraint since a type only needs to have the member Item (get/set) so I tried this
type Slice<'a, 'content when 'content: (member Item: int -> 'a)>
This still throw the usual error
So is it possible to constrain a type to still be generic but constraint to be indexable?
I think something like this should work:
type Slice<'a, 'content when 'content: (member get_Item: int -> 'a)> =
{
Content : 'content
Start : int
Stop : int
}
with
member inline slice.get_Item(i) =
slice.Content.get_Item(slice.Start + i)
I've implemented get_Item on Slice as well, so you can take a slice of a slice. Here are some values of this type:
let strSlice =
{
Content = "hello"
Start = 1
Stop = 2
}
let arraySlice =
{
Content = [| 2; 4; 6; 8 |]
Start = 0
Stop = 3
}
let strSliceSlice =
{
Content = strSlice
Start = 0
Stop = 1
}
[<Interface>]
type Indexable<'a> =
abstract member Item: int -> 'a with get
[<Struct>]
type Slice<'a> =
{
content: Indexable<'a>
start: int
len: int
}
with
interface Indexable<'a> with
member I.Item with get(i) = I.[idx]
member S.Item with get(idx) =
if idx >= S.len
then raise(IndexOutOfRangeException())
else S.content.[S.start+idx]
This works.

Match inside match - ocaml raises syntax error

Does anyone know why this function raises the syntax error? I haven't provided my written side functions, since they are probably not that relevant here, since it's revolving around proper syntax.
I tried deleting the brackets that raised the error (which I think.. should be there?), only to then raise another syntax error one line lower, at the begining of the row with the line "|".
type 'a grid = 'a Array.t Array.t
type problem = { initial_grid : int option grid }
type available = { loc : int * int; possible : int list }
type state = { problem : problem; current_grid : int option grid; available = available list }
let branch_state (state : state) : (state * state) option =
if prazni_kvadratki state.current_grid = [] then
None
else
let lst = prazni_kvadratki state.current_grid in
let loc = List.hd lst in
let st1_grid = copy_grid state.current_grid in
let st2_grid = copy_grid state.current_grid in
match razpolozljive state.current_grid loc with
| x :: xs -> (vstavi_vrednost st1_grid loc (Some x);
let st1 = {problem = state.problem; current_grid = st1_grid} in
match xs with
| [y] -> (vstavi_vrednost st2_grid loc (Some y);
let st2 = {
problem = state.problem;
current_grid = st2_grid
}) (* this is where it shows me a syntax error*)
| y :: ys -> let st2 = {
problem = state.problem;
current_grid = copy_grid state.current_grid;
available = {loc = loc; possible = xs}
})
Some (st1, st2)
On around the 5th last line or so you have let with no matching in. The let expression always must have an in.
The basic rule for nested match is that you should use parentheses or begin/end around the inner one:
match x with
| [] -> 0
| [_] ->
begin
match y with
| [] -> 1
| _ -> 2
end
| _ -> 3
Otherwise the final cases of the outer match look like they belong to the inner one. I don't think this is your problem here because you have no outer cases after the inner match.
Syntax issues
You have a few syntax issues.
type state = { problem : problem; current_grid : int option grid; available = available list }
You likely meant to have:
type state = { problem : problem; current_grid : int option grid; available : available list }
However, given how you construct values later in your program where you provide a value for the available field in one case but not in the other, you may want a variant type that allows your state type to be constructed with or without this value, with distinct behavior when not constructed with this value. This might look like:
type state =
| With_available of { problem : problem;
current_grid : int option grid;
available : available list }
| Without_available of { problem : problem;
current_grid : int option grid }
The other syntax issue is missing an in to go with a let which brings us to:
Scoping issues
There are clearly some miunderstandings here for you in regards to how scope works with let bindings in OCaml.
Aside from a definition at the topmost level of a program, all let bindings are local bindings. That is, they apply to a single expression that trails an in keyword.
Consider this toplevel session.
# let x = 5;;
val x : int = 5
# let y =
let x = 42 in
x + 3;;
val y : int = 45
# x;;
- : int = 5
#
Here the x bound with let x = 42 in x + 3 is only in scope for the duration of the expression x + 3. Once we're done with that expression, that binding for x is gone. In the outer scope, x is still bound to 5.
In both cases in your match you bind names st1 and st2, which would have to be local bindings, but then you try to use them in an outer scope, where they don't exist.
If you want st1 and st2, you'd need to bind them in a similar way to a and b in the below simple example.
# let (a, b) = match [1; 2; 3] with
| [x] -> (x, x)
| x :: y :: _ -> (x, y)
| _ -> (1, 1)
in
a + b;;
- : int = 3
#
Pattern-matching
Please also note that the pattern-matching you're shown is not exhaustive. It does not handle an empty list. If you consider it impossible that an empty list will be a result, you still have to either handle it anyway or use a different data structure than a list which can by definition be empty.
You've shown pattern-matching of the basic pattern:
match some_list with
| x :: xs ->
match xs with
| [y] -> ...
| y :: xs -> ...
We can actually match against the two possibilities you've show in one level of match.
match some_list with
| x :: [y] -> ...
| x :: y :: ys -> ...
If you still need to address y :: ys as xs in the second case, we can readily bind that name with the as keyword.
match some_list with
| x :: [y] -> ...
| x :: (y :: ys as xs) -> ...

Dealing with too many terminal nodes in grammar

I'm trying to write a parser for protobuf3 using the grammars from https://github.com/antlr/grammars-v4/blob/master/protobuf3/Protobuf3.g4.
and I'm trying to deal with the _type declaration in my grammar:
field
: ( REPEATED )? type_ fieldName EQ fieldNumber ( LB fieldOptions RB )? SEMI
;
type_
: DOUBLE
| FLOAT
| INT32
| INT64
| UINT32
| UINT64
| SINT32
| SINT64
| FIXED32
| FIXED64
| SFIXED32
| SFIXED64
| BOOL
| STRING
| BYTES
| messageDefinition
| enumType
;
Inside enterField I have this snippet:
#Override
public void enterField(Protobuf3Parser.FieldContext ctx) {
MessageDefinition messageDefinition = this.messageStack.peek();
Field field = new Field();
field.setName(ctx.fieldName().ident().getText());
field.setPosition(ctx.fieldNumber().getAltNumber());
messageDefinition.addField(field);
super.enterField(ctx);
}
However I'm not sure on how I can deal with the type_ context here. It has too many terminal nodes (for basic types) and it could have a messageType or an enumType.
For my use case all I care about is if it is a basic type (and in that case get the type name) or if it is a complex type (such as another message or enum) get the definition name.
Is there a way to do this without having to check each possible outcome of ctx.field_() ?
Thank you
If both, messageDefinition and enumType return single lexer token, you can make the entire access very easy by using a label:
type_
: value = DOUBLE
| value = FLOAT
| value = INT32
| value = INT64
| value = UINT32
| value = UINT64
| value = SINT32
| value = SINT64
| value = FIXED32
| value = FIXED64
| value = SFIXED32
| value = SFIXED64
| value = BOOL
| value = STRING
| value = BYTES
| value = messageDefinition
| value = enumType
;
With that you only need to use the field value:
#Override
public void enterField(Protobuf3Parser.FieldContext ctx) {
...
const type = ctx.type_().value.getText();
...
super.enterField(ctx);
}

How can I have multiple iterators to the same data pertaining to a file?

I have a file that I wish to read and filter the data into two different sets and determine the number of items in each set.
use std::io::{self, BufRead};
fn main() {
let cursor = io::Cursor::new(b"pillow\nbrick\r\nphone");
let lines = cursor.lines().map(|l| l.unwrap());
let soft_count = lines.filter(|line| line.contains("pillow")).count();
let hard_count = lines.filter(|line| !line.contains("pillow")).count();
}
Playground
GitHub
However, the borrow checker gives me an error:
error[E0382]: use of moved value: `lines`
--> src/main.rs:14:22
|
8 | let lines = cursor.lines().map(|l| l.unwrap());
| ----- move occurs because `lines` has type `std::iter::Map<std::io::Lines<std::io::Cursor<&[u8; 19]>>, [closure#src/main.rs:8:36: 8:50]>`, which does not implement the `Copy` trait
9 |
10 | let soft_count = lines
| ----- value moved here
...
14 | let hard_count = lines
| ^^^^^ value used here after move
I tried getting around this using reference counting to allow multiple ownership:
use std::io::{self, BufRead};
use std::rc::Rc;
fn main() {
let cursor = io::Cursor::new(b"pillow\nbrick\r\nphone");
let lines = Rc::new(cursor.lines().map(|l| l.unwrap()));
let soft_count = Rc::clone(&lines)
.filter(|line| line.contains("pillow"))
.count();
let hard_count = Rc::clone(&lines)
.filter(|line| !line.contains("pillow"))
.count();
}
Playground
Github
I get a similar error message:
error[E0507]: cannot move out of an `Rc`
--> src/main.rs:11:22
|
11 | let soft_count = Rc::clone(&lines)
| ^^^^^^^^^^^^^^^^^ move occurs because value has type `std::iter::Map<std::io::Lines<std::io::Cursor<&[u8; 19]>>, [closure#src/main.rs:9:44: 9:58]>`, which does not implement the `Copy` trait
error[E0507]: cannot move out of an `Rc`
--> src/main.rs:15:22
|
15 | let hard_count = Rc::clone(&lines)
| ^^^^^^^^^^^^^^^^^ move occurs because value has type `std::iter::Map<std::io::Lines<std::io::Cursor<&[u8; 19]>>, [closure#src/main.rs:9:44: 9:58]>`, which does not implement the `Copy` trait
You cannot. Instead, you will need to clone the iterator, or some building block of it. In this case, the highest thing you can clone is the Cursor:
use std::io::{self, BufRead};
fn main() {
let cursor = io::Cursor::new(b"pillow\nbrick\r\nphone");
let lines = cursor.clone().lines().map(|l| l.unwrap());
let lines2 = cursor.lines().map(|l| l.unwrap());
let soft_count = lines.filter(|line| line.contains("pillow")).count();
let hard_count = lines2.filter(|line| !line.contains("pillow")).count();
}
For an actual File, you will need to use try_clone as it might fail. In either case, you will be referring to the same data twice and only the iterator information will be kept.
For your specific case, you don't need any of this. In fact, iterating over the data twice is inefficient. The simplest built-in thing you can do is to partition the iterator:
let (softs, hards): (Vec<_>, Vec<_>) = lines.partition(|line| line.contains("pillow"));
let soft_count = softs.len();
let hard_count = hards.len();
This is still a bit inefficient as you don't need the actual values. You could create your own type that implements Extend and discards the values:
#[derive(Debug, Default)]
struct Count(usize);
impl<T> std::iter::Extend<T> for Count {
fn extend<I>(&mut self, iter: I)
where
I: IntoIterator,
{
self.0 += iter.into_iter().count();
}
}
let (softs, hards): (Count, Count) = lines.partition(|line| line.contains("pillow"));
let soft_count = softs.0;
let hard_count = hards.0;
You could also just use a for loop or build something on top of fold:
let (soft_count, hard_count) = lines.fold((0, 0), |mut state, line| {
if line.contains("pillow") {
state.0 += 1;
} else {
state.1 += 1;
}
state
});

Recursive Set in OCaml

how can I manage to define a Set in OCaml that can contains element of its type too?
To explain the problem I have a type declaration for a lot of data types like
type value =
Nil
| Int of int
| Float of float
| Complex of Complex.t
| String of string
| Regexp of regexp
| Char of char
| Bool of bool
| Range of (int*int) list
| Tuple of value array
| Lambda of code
| Set of ValueSet.t (* this isn't allowed in my case since module is declared later*)
In addition I declare a concrete module for ValueSet later in the same file:
module ValueSet = Set.Make(struct type t = value let compare = Pervasives.compare end)
The problem is that ValueSet has value as it's elt type but value can be a ValueSet so I'm getting troubles while trying to compile it.
All of these declarations are contained in just a file named types.ml (that has it's own interface types.mli but without any ValueSet module decl since I'm not either sure it's possible).
Can this problem be solved in some way?
You can use recursive modules. Language manual uses precisely the same example of recursive set type to illustrate this language feature. Below is a relevant excerpt.
A typical example of a recursive module definition is:
module rec A : sig
type t = Leaf of string | Node of ASet.t
val compare: t -> t -> int
end
= struct
type t = Leaf of string | Node of ASet.t
let compare t1 t2 =
match (t1, t2) with
(Leaf s1, Leaf s2) -> Pervasives.compare s1 s2
| (Leaf _, Node _) -> 1
| (Node _, Leaf _) -> -1
| (Node n1, Node n2) -> ASet.compare n1 n2
end
and ASet : Set.S with type elt = A.t
= Set.Make(A)
It can be given the following specification:
module rec A : sig
type t = Leaf of string | Node of ASet.t
val compare: t -> t -> int
end
and ASet : Set.S with type elt = A.t