Thank you again! By using Go reflection, the code you provided is indeed 
simple and elegant.

Unfortunately, for better performance and less magic, the main motivation 
behind my validation library is to try to avoid using Go reflection. So you 
may have noticed that I have used lots of type assertions for the currently 
non-generic implementation.

With the help of Go generics, the implementation code certainly can be 
largely simplified and can handle much more non-primitive types properly. 
For now, I think providing both `LenSlice` and `LenMap` may be also 
good, although not as good as the single `Len`.

On Saturday, March 19, 2022 at 11:53:20 AM UTC+8 Henry wrote:

> I don't think there is such support in Go generics as of now. I may be 
> wrong though. 
>
> The alternative is to use reflection. Here is the simplified code of your 
> example:
>
> ```go
> func GetValidator(field Field) Validator {
>         return Validator{
>                 Validate: func() (int, error) {
>                         value := reflect.ValueOf(field.Value)
>                         if value.Kind() != reflect.Map && value.Kind() != 
> reflect.Slice && value.Kind() != reflect.Array {
>                                 return 0, errors.New("invalid type")
>                         }
>                         return value.Len(), nil
>                 },
>         }
> }
> ```
>
> On Friday, March 18, 2022 at 5:47:29 PM UTC+7 RussellLuo wrote:
>
>> Thanks for your reply, Henry!
>>
>> I admit that my problem seems a little bit silly without providing 
>> the contextual information. Actually I am trying to rewrite my little 
>> [validation library](https://github.com/RussellLuo/validating) by 
>> leveraging Go generics.
>>
>> For the originally non-generic [Len](
>> https://pkg.go.dev/github.com/RussellLuo/validating/v2#Len) validator 
>> factory, I have implemented two generic versions. One for a slice:
>>
>> ```go
>> // LenSlice is a leaf validator factory used to create a validator, which 
>> will
>> // succeed when the length of the slice field is between min and max.
>> func LenSlice[T ~[]E, E any](min, max int) (mv *MessageValidator) {
>>         mv = &MessageValidator{
>>                 Message: "with an invalid length",
>>                 Validator: Func(func(field *Field) Errors {
>>                         v, ok := field.Value.(T)
>>                         if !ok {
>>                                 return NewUnsupportedErrors(field, 
>> "LenSlice")
>>                         }
>>
>>                         l := len(v)
>>                         if l < min && l > max {
>>                                 return NewErrors(field.Name, ErrInvalid, 
>> mv.Message)
>>                         }
>>                         return nil
>>                 }),
>>         }
>>         return
>> }
>> ```
>>
>> and the other for a map:
>>
>> ```go
>> // LenMap is a leaf validator factory used to create a validator, which 
>> will
>> // succeed when the length of the map field is between min and max.
>> func LenMap[T map[K]V, K comparable, V any](min, max int) (mv 
>> *MessageValidator) {
>>         mv = &MessageValidator{
>>                 Message: "with an invalid length",
>>                 Validator: Func(func(field *Field) Errors {
>>                         v, ok := field.Value.(T)
>>                         if !ok {
>>                                 return NewUnsupportedErrors(field, 
>> "LenMap")
>>                         }
>>
>>                         l := len(v)
>>                         if l < min && l > max {
>>                                 return NewErrors(field.Name, ErrInvalid, 
>> mv.Message)
>>                         }
>>                         return nil
>>                 }),
>>         }
>>         return
>> }
>> ```
>>
>> As a result, we can use them as below:
>>
>> ```go
>> Schema{
>>         F("slice", []string{"foo"}): LenSlice[[]string](0, 2),
>>         F("map", map[string]int{"a": 1, "b": 2}): 
>> LenMap[map[string]int](0, 2),
>> }
>> ```
>>
>> *Now I am wondering if I can merge the above two versions into one 
>> generic `Len`, with the help of a constraint, say, SliceOrMap*. For 
>> example:
>>
>> ```go
>> func Len[T SliceOrMap](min, max int) (mv *MessageValidator) {
>>         mv = &MessageValidator{
>>                 Message: "with an invalid length",
>>                 Validator: Func(func(field *Field) Errors {
>>                         v, ok := field.Value.(T)
>>                         if !ok {
>>                                 return NewUnsupportedErrors(field, "Len")
>>                         }
>>
>>                         l := len(v)
>>                         if l < min && l > max {
>>                                 return NewErrors(field.Name, ErrInvalid, 
>> mv.Message)
>>                         }
>>                         return nil
>>                 }),
>>         }
>>         return
>> }
>> ```
>>
>> Then we can use the same `Len` for both a slice and a map as below 
>> instead:
>>
>> ```go
>> Schema{
>>         F("slice", []string{"foo"}): Len[[]string](0, 2),
>>         F("map", map[string]int{"a": 1, "b": 2}): Len[map[string]int](0, 
>> 2),
>> }
>> ```
>>
>> *(NOTE*: I have posted this reply earlier, but is was not displayed 
>> (maybe due to my wrong choice of "Reply to author").  Now I post it again 
>> by selecting "Reply all".)
>> On Friday, March 18, 2022 at 3:07:17 PM UTC+8 Henry wrote:
>>
>>> Have you considered this?
>>> ```go
>>> func IsBetween(value, min, max int) bool {
>>>    return value>=min && value <=max
>>> }
>>>
>>> if IsBetween(len(myMap), 10, 25) {
>>>   //do something
>>> }
>>> ```
>>>
>>> On Friday, March 18, 2022 at 11:20:37 AM UTC+7 RussellLuo wrote:
>>>
>>>> Hi there,
>>>>
>>>> Thanks to Go generics in 1.18, I can write a generic function 
>>>> `LenBetween` for a slice:
>>>>
>>>> ```go
>>>> func SliceLenBetween[T ~[]E, E any](s T, min, max int) bool {
>>>>         return len(s) >= min && len(s) <= max
>>>> }
>>>> ```
>>>>
>>>> as well as for a map:
>>>>
>>>> ```go
>>>> func MapLenBetween[T map[K]V, K comparable, V any](s T, min, max int) 
>>>> bool {
>>>>         return len(s) >= min && len(s) <= max
>>>> }
>>>> ```
>>>>
>>>> Is there any way to write a constraint, say, SliceOrMap, to support 
>>>> either a slice or a map?
>>>>
>>>> With the help of SliceOrMap, then I can write a more generic version 
>>>> `LenBetween` like this:
>>>>
>>>> ```go
>>>> func MapLenBetween[T SliceOrMap](s T, min, max int) bool {
>>>>         return len(s) >= min && len(s) <= max
>>>> }
>>>> ```
>>>>
>>>> Thanks in advance!
>>>>
>>>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/f49ff0f3-22fe-4435-92ef-d6155f0e29f4n%40googlegroups.com.

Reply via email to