Adding functions to TH to make CamelCase to under_score easier. #155

Merged
merged 5 commits into from Jan 2, 2014

Conversation

Projects
None yet
4 participants
@heyzua
Contributor

heyzua commented Oct 13, 2013

I have noticed in multiple places around hackage that people add
these kinds of functions to get their JSON formatted keys (often
underscore_formatted_strings) out of Haskell's CamelCase data
properties (often withLongStringsOfCAPITALS).

I have also added to the .gitignore those files generated by the new
cabal 1.18 'sandbox' feature.

I have a few outstanding questions:

  1. Should these functions live in another module with an isomorphic
    inverse function, like underscoreToCamel? Like Aeson.Properties?
  2. I am at something of a loss as to how to integrate testing into the
    suite. I've tested the out at the REPL, but what steps should be
    taken to integrate these into QuickCheck?
Gabe McArthur
Adding functions to TH to make CamelCase to under_score easier.
I have noticed in multiple places around hackage that people add
these kinds of functions to get their JSON formatted keys (often
underscore_formatted_strings) out of Haskell's CamelCase data
properties (often withLongStringsOfCAPITALS).

I have also added to the .gitignore those files generated by the new
cabal 1.18 'sandbox' feature.

I have a few outstanding questions:

1) Should these functions live in another module with an isomorphic
inverse function, like `underscoreToCamel`? Like Aeson.Properties?
2) I am at something of a loss as to how to integrate testing into the
suite. I've tested the out at the REPL, but what steps should be
taken to integrate these into QuickCheck?
@basvandijk

This comment has been minimized.

Show comment Hide comment
@basvandijk

basvandijk Oct 24, 2013

Collaborator

Hi @GabeMc , this looks like a useful function to have. A few comments:

  • I would define this function near the same place as were the Options type is defined. That's currently the Data.Aeson.Types.Internal module. (Note that not only the TH deriver uses the Options type but also the GHC Generics based deriver)

  • It would be nice if the character to replace the camelCasing could be parameterized as in:

    camelTo :: Char -> (String -> String)
  • Why combine the dropping of the prefix with underscoring. I think it's easier if the user just uses function composition as in:

    defaultOptions {fieldLabelModifier = camelTo '_' . drop 4}

What do you think?

Collaborator

basvandijk commented Oct 24, 2013

Hi @GabeMc , this looks like a useful function to have. A few comments:

  • I would define this function near the same place as were the Options type is defined. That's currently the Data.Aeson.Types.Internal module. (Note that not only the TH deriver uses the Options type but also the GHC Generics based deriver)

  • It would be nice if the character to replace the camelCasing could be parameterized as in:

    camelTo :: Char -> (String -> String)
  • Why combine the dropping of the prefix with underscoring. I think it's easier if the user just uses function composition as in:

    defaultOptions {fieldLabelModifier = camelTo '_' . drop 4}

What do you think?

@heyzua

This comment has been minimized.

Show comment Hide comment
@heyzua

heyzua Oct 26, 2013

Contributor

Good idea, @basvandijk. I like it much better. However, my question about testing remains. I've tested it, and it seems to work, but that hardly seems sufficient. Where would be the best place to insert a test about its functionality?

Contributor

heyzua commented Oct 26, 2013

Good idea, @basvandijk. I like it much better. However, my question about testing remains. I've tested it, and it seems to work, but that hardly seems sufficient. Where would be the best place to insert a test about its functionality?

@basvandijk

This comment has been minimized.

Show comment Hide comment
@basvandijk

basvandijk Oct 27, 2013

Collaborator

I like it much better.

I like it to! Note you have a small spelling error: s/interpsersing/interspersing/.

However, my question about testing remains. I've tested it, and it seems to work, but that hardly seems sufficient. Where would be the best place to insert a test about its functionality?

You should add a test to the test-suite. Its main file is tests/Properties.hs

Collaborator

basvandijk commented Oct 27, 2013

I like it much better.

I like it to! Note you have a small spelling error: s/interpsersing/interspersing/.

However, my question about testing remains. I've tested it, and it seems to work, but that hardly seems sufficient. Where would be the best place to insert a test about its functionality?

You should add a test to the test-suite. Its main file is tests/Properties.hs

@bos

This comment has been minimized.

Show comment Hide comment
@bos

bos Nov 22, 2013

Owner

If you can rebase these patches so that they apply cleanly, I'll snag 'em.

Owner

bos commented Nov 22, 2013

If you can rebase these patches so that they apply cleanly, I'll snag 'em.

Merge branch 'master' of https://github.com/bos/aeson
Conflicts:
	Data/Aeson/Types/Internal.hs
@heyzua

This comment has been minimized.

Show comment Hide comment
@heyzua

heyzua Nov 29, 2013

Contributor

Ok, I think that should do it.

Contributor

heyzua commented Nov 29, 2013

Ok, I think that should do it.

bos added a commit that referenced this pull request Jan 2, 2014

Merge pull request #155 from gabemc/master
Adding functions to TH to make CamelCase to under_score easier.

@bos bos merged commit be4cfc8 into bos:master Jan 2, 2014

@hvr

This comment has been minimized.

Show comment Hide comment
@hvr

hvr May 25, 2014

Collaborator

@basvandijk

Why combine the dropping of the prefix with underscoring. I think it's easier if the user just uses function composition as in:

defaultOptions {fieldLabelModifier = camelTo '_' . drop 4}

fyi, while that's easier, I use something like the following in my code, as I want to catch typos (which can easily creep in when I copy'n'paste and forget to update the drop 4 to the proper prefix-length):

deCaml :: String -> String -> String
deCaml pfx s0
  | Just (c:cs) <- stripPrefix pfx s0, isUpper c = toLower c : toSnakeCase cs
  | otherwise = error "deCaml prefix-mismatch"
  where
    toSnakeCase = concatMap (\c -> if isUpper c then ['_',toLower c] else [c])
Collaborator

hvr commented May 25, 2014

@basvandijk

Why combine the dropping of the prefix with underscoring. I think it's easier if the user just uses function composition as in:

defaultOptions {fieldLabelModifier = camelTo '_' . drop 4}

fyi, while that's easier, I use something like the following in my code, as I want to catch typos (which can easily creep in when I copy'n'paste and forget to update the drop 4 to the proper prefix-length):

deCaml :: String -> String -> String
deCaml pfx s0
  | Just (c:cs) <- stripPrefix pfx s0, isUpper c = toLower c : toSnakeCase cs
  | otherwise = error "deCaml prefix-mismatch"
  where
    toSnakeCase = concatMap (\c -> if isUpper c then ['_',toLower c] else [c])
@basvandijk

This comment has been minimized.

Show comment Hide comment
@basvandijk

basvandijk May 26, 2014

Collaborator

I agree that drop 4 is not the best way to drop prefixes. In fact, I use the following at work for this:

delPrefix :: String -> (String -> String)
delPrefix prefix = \fieldName ->
    case stripPrefix prefix fieldName of
      Just ccs@(c:cs)
          | isUpper c -> toLower c : cs
          | null prefix -> ccs
          | otherwise -> error $ "The field name after the prefix " ++
                                 "must be written in CamelCase"
      Just [] -> error $ "The field name after the prefix may not be empty"
      Nothing -> error $ "The field name " ++ quotes fieldName ++
                         " does not begin with the required prefix " ++
                         quotes prefix

What I was arguing for was to not combine the dropping of a prefix with decamelcasification. They can simply be composed together if desired:

camelTo '_' . delPrefix "prefix"
Collaborator

basvandijk commented May 26, 2014

I agree that drop 4 is not the best way to drop prefixes. In fact, I use the following at work for this:

delPrefix :: String -> (String -> String)
delPrefix prefix = \fieldName ->
    case stripPrefix prefix fieldName of
      Just ccs@(c:cs)
          | isUpper c -> toLower c : cs
          | null prefix -> ccs
          | otherwise -> error $ "The field name after the prefix " ++
                                 "must be written in CamelCase"
      Just [] -> error $ "The field name after the prefix may not be empty"
      Nothing -> error $ "The field name " ++ quotes fieldName ++
                         " does not begin with the required prefix " ++
                         quotes prefix

What I was arguing for was to not combine the dropping of a prefix with decamelcasification. They can simply be composed together if desired:

camelTo '_' . delPrefix "prefix"
@hvr

This comment has been minimized.

Show comment Hide comment
@hvr

hvr May 27, 2014

Collaborator

What I was arguing for was to not combine the dropping of a prefix with decamelcasification. They can simply be composed together

+1 to that :-)

Collaborator

hvr commented May 27, 2014

What I was arguing for was to not combine the dropping of a prefix with decamelcasification. They can simply be composed together

+1 to that :-)

tolysz pushed a commit to tolysz/aeson that referenced this pull request May 18, 2015

Merge pull request #155 from gabemc/master
Adding functions to TH to make CamelCase to under_score easier.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment