let ( .![] ) obj f = f obj
type person = { id : int; name : string }
let id { id; _ } = id
let bob = { id = 1; name = "Bob" }
let next_id = bob.![id].![succ]
==> 2
let ( .![] ) obj f = f obj
type person = { id : int; name : string }
let id { id; _ } = id
let bob = { id = 1; name = "Bob" }
let next_id = bob.![id].![succ]
==> 2
Haha, what a coincidence, just did the same very recently while translating a rust library to OCaml: ego/generic.ml at 5daf312f8a444f9abcde5996c671b9282727a972 · Gopiandcode/ego · GitHub
let eclasses = eg.@[eclasses] in
let cost_map = Id.Map.create 10 in
let node_total_cost node =
let has_cost id = Id.Map.mem cost_map (eg.@[find] id) in
if List.for_all has_cost (L.children node)
then let cost_f id = fst @@ Id.Map.find cost_map (eg.@[find] id) in Some (E.cost cost_f node)
else None in
(* ... *)
with .@[]
defined as:
let (.@[]) self fn = fn self [@@inline always]
for bonus(?) points, you can name the first parameter self:
let add_enode self (node: Id.t L.shape) =
let node = self.@[canonicalise] node in
(* ... *)
I don’t normally write code like this in OCaml, but in this case, it made porting from rust easier, because the code mostly looked the same.
Huh, interesting! I made mine mostly as a ‘Lol I can’t believe I can do this’ kind of thing, although I did make a couple of useful Yojson helper ops that let me do this:
match json.$["reasons"].@[0].$["message"] with
| `String message -> Error (`Msg message)
| _ -> Error (`Msg "Failed to get JSON decode error reason")
You can use the multiple-indexing syntax to implement slicing (well, technically subs) sugar:
let (.:[;..]) s = function
| [|start; finish|] -> String.sub s start (finish - start)
| _ -> raise (Invalid_argument "slice takes exactly two indexes")
# "hello world".:[1;5];;
- : string = "ello"
The new indexing syntax is quite versatile :>
Yeah, same - normally I just use it for simplifying lookups:
let (.@[]) vl key = List.assoc key vl
I guess that’s the idiomatic usage probably - feels a bit more proper than using them to implement a sort of OOP.
Oh wow, this is perfect! brb, off to reimplement the python slicing semantics in OCaml:
let (.@[;..]) ls = function[@warning "-8"]
| [| start; -1 |] ->
List.to_iter ls
|> Iter.zip_i
|> Iter.drop_while (Pair.fst_map ((>) start))
|> Iter.map snd
| [| start; finish |] ->
List.to_iter ls
|> Iter.zip_i
|> Iter.drop_while (Pair.fst_map ((>) start))
|> Iter.take_while (Pair.fst_map ((>) finish))
|> Iter.map snd
| [| start; finish; step |] ->
List.to_iter ls
|> Iter.zip_i
|> Iter.drop_while (Pair.fst_map ((>) start))
|> Iter.take_while (Pair.fst_map ((>) finish))
|> Iter.filter (Pair.fst_map (fun ind -> (ind - start) mod step = 0))
|> Iter.map snd
Wow! And to think I was thinking of asking the OCaml devs to implement it like F# did!
This reminds me, I once cooked up this usage with lenses:
#require "lens.ppx_deriving";;
module Address = struct
type t = { street : string; city : string }
[@@deriving lens]
end
module Person = struct
type t = { id : string; name : string; address : Address.t }
[@@deriving lens]
end
let ( /: ) first second = Lens.compose second first
let ( .:[] ) obj lens = lens.Lens.get obj
let ( .:[]<- ) obj lens value = lens.Lens.set value obj
let bob = {
Person.id = "1";
name = "Bob";
address = {
Address.street = "1 Way St";
city = "Cityville";
};
}
let bob2 = bob.:[Person.address /: Address.street] <- "Rodeo Dr"
==>
val bob2 : Person.t = {
Person.id = "1";
name = "Bob";
address = {
Address.street = "Rodeo Dr";
city = "Cityville";
};
}
EDIT: with a little difflist magic, this could probably become just:
let bob2 = bob.:[[Person.address; Address.street]] <- "Rodeo Dr"
There’s something similar in the language already, in the form of an infix operator:
let next_id = bob |> id |> succ