Is there a good way to iterate, fold, or loop over all elements in a record that have the same type? For example, in the following OCaml code
type foo = { a : int; b : float; c : int; d : float } let foo = { a = 1; b = 2.0; c = 3; d = 4.0 }
I d like to iterate over all of the integers or floats separately. I am aware of Fieldslib, but it doesn t seem to do what I d like. For example, with Fieldslib, I can write the code:
open Fieldslib type bar = { w : int; x : float; y : int; z : float } with fields let bar = { w = 1; x = 2.0; y = 3; z = 4.0 } let print_int bar x = Printf.printf "%d " (Fieldslib.Field.get x bar) let print_float bar x = Printf.printf "%f " (Fieldslib.Field.get x bar);; Fields_of_bar.iter ~w:(print_int bar) ~x:(print_float bar) ~y:(print_int bar) ~z:(print_float bar)
But this forces us to iterate over all elements and not just the ints or floats separately. It also requires the function to be called on each element be specified separately. Really, I d like to just say iterate, map, or fold some function to the structure.
If this is difficult with a heterogeneous record type, would it be any easier with a record type where all elements have a single type? In this case, we could define a lens to project out all of the ints, floats, etc.
In some sense, it feels like I want some sort of purely functional iterator that works with record types and if such a technique exists I m not sure what it s called.
Edit 1
Actually, this may be a lot more straight forward than I thought. Fieldslib gives first class elements already. As long as we make a list of all the ones with the same type, we should be fine. In other words:
open Fieldslib type bar = { w : int; x : float; y : int; z : float } with fields let ints=[w;y] let floats=[x;z] let bar = { w = 1; x = 2.0; y = 3; z = 4.0 };; let print_ints bar = List.iter (fun l -> Printf.printf "%d " (l bar)) ints let print_floats bar = List.iter (fun l -> Printf.printf "%f " (l bar)) floats
More generally, a list of lenses with the same type should make all of this possible since we can then just apply the map, fold, and iter functions from lists.
Edit 2
In case someone wants to run @j-abrahamson s code, here s some mild modifications, so that it ran on my machine
{-# LANGUAGE RankNTypes #-} import Control.Applicative import Control.Monad.Identity data Foo = Foo { a :: Int, b :: Float, c :: Int, d :: Float } deriving Show type Traversal s a = forall f . Applicative f => (a -> f a) -> (s -> f s) intsOfFoo :: Traversal Foo Int intsOfFoo inj foo = build <$> inj (a foo) <*> inj (c foo) where build a c = foo { a = a, c = c } mapOf :: Traversal s a -> (a -> a) -> (s -> s) mapOf trav f = runIdentity . trav (Identity . f) foo0 = Foo { a = 1, b = 1, c = 1, d = 1 } foo1 = mapOf intsOfFoo (+1) foo0
I m also adding the Haskell flag in case this helps anyone else.