How to iterate over record fields?
Asked Answered
F

1

8

I have following datatype defined as record

data Config = Config
  { field1 :: String
  , field2 :: String
  , field3 :: String
  }

I want to iterate over each field of Config, apply some function String -> String, for example tail and get in return new Config.

What is idiomatic way to do this? Preferably, without heavy 3rd party libraries.

Fushih answered 7/10, 2015 at 17:44 Comment(6)
The head function returns a Char. Am I correct to understand that you want the type of your fields to change depending on what the supplied function returns?Taphole
uniplate is a lightweight generics library that can probably do that easily.Nahtanha
@MichaelSteele my fault, something like tail which will return same type String. I've updated questionsFushih
In the case of tail, @leftroundabout's answer is the one to use. Note that even though you are looking for a function Config -> Config, you'll want to add a type variable to the definition of Config (as in @leftroundabout's answer) so that fmap can do the work for you.Krongold
Are you sure you aren't thinking by analogy with a dynamically typed language where this would be a natural thing to do. In Haskell its not impossible, but in general each field of a data structure would represent a different type of thing, and hence be processed with a different function. The look of your structure seems to suggest a dictionary or array, in which case Data.Map or Array might be more suitable.Cutler
@PaulJohnson Yes. I'm sure. My config fields have 3 different kind of data inside yaml field - "data", "data:data" and "data:data:data". Since, I don't want to write parser, I'm using decodeFile one-liner. It's easier for me to read through config to datatype and then provide case logic for fields afterwardsFushih
W
16

Well, the best way to do it would probably be

{-# LANGUAGE DeriveFunctor #-}

type Config = Config' String
data Config' a = Config
  { field1 :: a
  , field2 :: a
  , field3 :: a
  } deriving (Functor)

configHeads :: Config -> Config' Char
configHeads = fmap head
Weiss answered 7/10, 2015 at 18:14 Comment(2)
monotraversable would be an alternative.Lyric
For strings, this seems like the obvious way to go. It's unfortunate that this approach prevents nice optimizations in some other situations.Lactoflavin

© 2022 - 2024 — McMap. All rights reserved.