Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

types - recurse over same type? #103

Open
soupi opened this issue Jan 2, 2020 · 5 comments
Open

types - recurse over same type? #103

soupi opened this issue Jan 2, 2020 · 5 comments

Comments

@soupi
Copy link

soupi commented Jan 2, 2020

Hi! First I'd like to thank you for this super cool package!

I was wondering why doesn't types recurse deeper when the Traversal focus has the same type as the "big" outer type.

Example:

{-# language LambdaCase, DeriveDataTypeable, DeriveGeneric, TypeApplications #-}

import GHC.Generics
import Data.Data

data Expr
  = Lit Int
  | Add Expr Expr
  | Sub Expr Expr
  | Mul Expr Expr
  deriving (Show, Data, Generic)

expr :: Expr
expr = Add (Sub (Lit 1) (Lit 2)) (Lit 3)

f :: Expr -> Expr
f = \case
    Add a b -> Mul a b
    Lit i -> Lit (i + 1)
    a -> a

When I run over types f expr i get: Mul (Sub (Lit 1) (Lit 2)) (Lit 3) - meaning only the outer Expr has been transformed (shallow).

When I run over (types @Int) (+1) expr I get: Add (Sub (Lit 2) (Lit 3)) (Lit 4) - meaning all of the Ints have been transformed (deep).

Coming from uniplate and Control.Lens.Plated I'd expect over types to work similarly to transformBi and toListOf types to work like universeBi, but it only doesn't when the focus type is the same:

λ> import Data.Generics.Uniplate.Data
λ> import Data.Generics.Product
λ> import Control.Lens

λ> over (types @Int) (+1) expr
Add (Sub (Lit 2) (Lit 3)) (Lit 4)
λ> transformBi ((+1) :: Int -> Int) expr
Add (Sub (Lit 2) (Lit 3)) (Lit 4)

λ> over types f expr
Mul (Sub (Lit 1) (Lit 2)) (Lit 3)
λ> transformBi f expr
Mul (Sub (Lit 2) (Lit 3)) (Lit 4)

λ> universeBi expr :: [Int]
[1,2,3]
λ> toListOf (types @Int) expr
[1,2,3]

λ> toListOf (types @Expr) expr
[Add (Sub (Lit 1) (Lit 2)) (Lit 3)]
λ> universeBi expr :: [Expr]
[Add (Sub (Lit 1) (Lit 2)) (Lit 3),Sub (Lit 1) (Lit 2),Lit 1,Lit 2,Lit 3]

Any reason for the behaviour differences?

Thanks in advance!

@Lysxia
Copy link
Collaborator

Lysxia commented Jan 3, 2020 via email

@soupi
Copy link
Author

soupi commented Jan 3, 2020

After further inspection following your tip regarding non-recursive traversals it seems like types has the same semantics as childrenBi and descendBi from uniplate:

λ> descendBi f expr
Mul (Sub (Lit 1) (Lit 2)) (Lit 3)
λ> descendBi ((+1) :: Int -> Int) expr
Add (Sub (Lit 2) (Lit 3)) (Lit 4)

λ> U.childrenBi expr :: [Expr]
[Add (Sub (Lit 1) (Lit 2)) (Lit 3)]
λ> U.childrenBi expr :: [Int]
[1,2,3]

@neongreen
Copy link

Is there a way to implement this using current types?

@soupi
Copy link
Author

soupi commented May 15, 2020

I might be mistaken, but I don't think you can.

@georgefst
Copy link

georgefst commented Mar 29, 2022

If this behaviour isn't changing any time soon (from the above discussion, it's not clear to me whether it even can be "fixed"), it would be great to at least have a comment in the documentation.

I've just spent a while debugging an issue where I was implicitly expecting types @a @a to recurse. I also for some reason assumed that the outermost expression wouldn't be traversed, but perhaps that part should have been more obvious. Either way, I think the behaviour could do with clarification.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants