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

Add reflection-related template functions #4081

Closed
moorereason opened this Issue Nov 14, 2017 · 11 comments

Comments

Projects
None yet
4 participants
@moorereason
Copy link
Contributor

moorereason commented Nov 14, 2017

moorereason added a commit to moorereason/hugo that referenced this issue Dec 15, 2017

tpl: Add reflect namespace
Adds KindIs, KindOf, TypeIs, TypeIsLike, and TypeOf funcs in a reflect
namespace.

Fixes gohugoio#4081

moorereason added a commit to moorereason/hugo that referenced this issue Dec 16, 2017

tpl: Add reflect namespace
Adds KindIs, KindOf, TypeIs, TypeIsLike, and TypeOf funcs in a reflect
namespace.

Fixes gohugoio#4081
@stale

This comment has been minimized.

Copy link

stale bot commented Feb 12, 2018

This issue has been automatically marked as stale because it has not had recent activity. The resources of the Hugo team are limited, and so we are asking for your help.
If this is a bug and you can still reproduce this error on the master branch, please reply with all of the information you have about it in order to keep the issue open.
If this is a feature request, and you feel that it is still relevant and valuable, please tell us why.
This issue will automatically be closed in the near future if no further activity occurs. Thank you for all your contributions.

@stale stale bot added the Stale label Feb 12, 2018

@kaushalmodi

This comment has been minimized.

Copy link
Member

kaushalmodi commented Feb 12, 2018

Oh no! Please keep this.

@stale stale bot removed the Stale label Feb 12, 2018

@kaushalmodi

This comment has been minimized.

Copy link
Member

kaushalmodi commented Feb 12, 2018

I have to resort to this:

{{ $value              := . }}
{{ $type               := (printf "%T" $value) }}
{{ $typeIsBool         := (eq "bool" $type) }}
{{ $typeIsNumber       := (or (eq "int" $type) (eq "int64" $type) (eq "float64" $type)) }}
{{ $typeIsString       := (eq "string" $type) }}
{{ $typeIsTime         := (eq "time.Time" $type) }}
{{ $typeIsSlice        := (findRE "^([[][]]|.*TaxonomyList|output\\.Formats|resource\\.Resources|\\*?hugolib\\.Menu$|\\*?hugolib\\.Pages$)" $type) }} <!-- match ^[] -->
{{ $typeIsMap          := (findRE "^(map[[].+[]]|.*SiteSocial|\\*hugolib\\.Menus|hugolib\\.AuthorList)" $type) }} <!-- match ^map[*] -->

{{ $typeIsSiteInfo     := (eq "*hugolib.SiteInfo" $type) }}
{{ $typeIsGitInfo      := (findRE "^.*gitmap\\.GitInfo" $type) }}
{{ $typeIsOutputFormat := (eq "output.Format" $type) }}
{{ $typeIsResource     := (findRE "^\\*resource" $type) }}
{{ $typeIsPage         := (findRE "^\\*hugolib\\.Page" $type) }}
{{ $typeIsFileInfo     := (findRE "^\\*(hugolib|source)\\.[fF]ileInfo$" $type) }} <!-- terms pages for e.g. uses source.FileInfo, regular pages using hugolib.fileInfo -->
{{ $typeIsMenuEntry    := (eq "*hugolib.MenuEntry" $type) }}
{{ $typeIsStruct       := (or $typeIsSiteInfo $typeIsGitInfo $typeIsOutputFormat $typeIsFileInfo $typeIsPage $typeIsMenuEntry) }}
{{ $typeIsInterface := (or $typeIsResource) }}

My current solution (above) is very hacky. As you see, there's no easy way to figure out if a variable is a slice/map/struct/interface.


Full code

@kaushalmodi

This comment has been minimized.

Copy link
Member

kaushalmodi commented Feb 12, 2018

Proposal for reflect template functions

@bep @moorereason

Here's a very rough proposal. For many Hugo users, they would generally be interested in knowing if a variable is a string (doesn't matter if it is string, or template.HTML, or template.URL, ..), or a number (doesn't matter int vs int64 vs float64 vs ..), or a slice (so that range can be used), or a map (so that range $i, $j := .. can be used), or just knowing that something is a struct or an interface (to know enough that range cannot be used).

To a user wanting to know these things in a template, it's not important (please comment below if this is wrong) to know a specific Type and Kind. The functions added to Hugo template functions are user-level functions, and not for internal Go debugging.. it's just to debug the Hugo layout templates and such.

With that introduction, I would propose adding these functions:

  • reflect.IsBool
  • reflect.IsNumber (if this is too coarse, then may be reflect.IsInteger and reflect.IsFloat?)
  • reflect.IsString
  • reflect.IsTime
  • reflect.IsSlice
  • reflect.IsMap
  • reflect.IsStruct
  • reflect.IsInterface

All of these functions should return a boolean value.

@regisphilibert

This comment has been minimized.

Copy link

regisphilibert commented May 3, 2018

That sounds great, but couldn't we have a function returning the type? (similar to javascript's typeof).

This means we could both "retrieve" the type and test it in conjunction with eq. And this limit the addition to one single template function.

{{ with .Params.stuff }}
   {{ if eq (getType .) "slice" }}
      {{ range . }}
       that code
      {{ end }}
   {{ end }}
{{ end}}
@kaushalmodi

This comment has been minimized.

Copy link
Member

kaushalmodi commented May 3, 2018

@regisphilibert That would be fine too, but I would still weigh on the reflect.Is.. functions as they look sweeter (I meant, a better syntactic sugar 😄).

It would be nicer to see {{ if reflect.IsSlice . }} instead of {{ if eq (getType .) "slice" }}. There too, if we go down the getType route, it would be good to add the reflect namespace, to prevent confusion with the Hugo types used in layouts and the variable Type (slice, map, etc.). But then it becomes even wordier..

{{ if reflect.IsSlice . }}

vs

{{ if eq (reflect.getType .) "slice" }}

Also, there are only handful data Types that we care about here.. so the effort to get all those reflect.Is.. should not be too bad.

@bep

This comment has been minimized.

Copy link
Member

bep commented May 3, 2018

@regisphilibert you can do similar with printf today, e.g. {{ if eq (printf "%T" .) "string" }}. But I think you will get a little dizzy about the myriad of types out there ...

As @kaushalmodi points out, you really do not care too much if it is a "slice of apples", just that it is a slice that you can iterate over, a map with keys/values etc.

@kaushalmodi

This comment has been minimized.

Copy link
Member

kaushalmodi commented May 3, 2018

But I think you will get a little dizzy about the myriad of types out there

He probably meant a "getKind", again not to confuse with page kinds :)

But yes, talking about the myriad of types, I am probably covering a veery small fraction of all Hugo types here ;-)

@regisphilibert

This comment has been minimized.

Copy link

regisphilibert commented May 3, 2018

Yeah, I in the end, all we really need is a CanWeRangeOnThis function.

I agree the boolean version of @kaushalmodi is cleaner and I'd happily settle for a reflect.getType.

@moorereason moorereason self-assigned this Nov 27, 2018

@moorereason moorereason added this to the v0.52 milestone Nov 27, 2018

@moorereason

This comment has been minimized.

Copy link
Contributor

moorereason commented Nov 27, 2018

I have a subset of @kaushalmodi's proposal ready.

I'm omitting IsTime because I'm sticking with Kind tests, and that would be a Type test. Use printf for Type tests.

I'm omitting IsInterface because I'm not yet convinced of its usefulness. How would this be useful?

I currently have IsBool, IsMap, IsNumber, IsSlice, IsString, and IsStruct.

Any comments before I submit a PR?

Cc: @bep

@bep

This comment has been minimized.

Copy link
Member

bep commented Nov 28, 2018

IsSlice and IsMap have clear use cases. I'm not convinced about the "general usefulness" of the others? And note that @kaushalmodi 's "debug printer" does not fall into the "general useful" category.

What is IsStruct used for?

@bep bep modified the milestones: v0.52, v0.53 Nov 28, 2018

moorereason added a commit to moorereason/hugo that referenced this issue Nov 28, 2018

tpl: Add reflect namespace
Add a reflect namespace that offers a two boolean functions for
testing if a value is a map or slice.

Fixes gohugoio#4081

@bep bep closed this in c84f506 Dec 7, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment