awalterschulze / goderive

Derives and generates mundane golang functions that you do not want to maintain yourself
Apache License 2.0
1.23k stars 44 forks source link

suggestion: Make functions that returns secondary bool composable #50

Closed ingun37 closed 5 years ago

ingun37 commented 5 years ago

Hello I think it would be useful if I can compose functions that returns secondary bool. Because sometimes I expect a map has a specific key, or an interface to be convertible into a type, or many other cases that we can consider it as error when false is returned from such functions. Heres some code I actually use to make it composable with deriveCompose. First one expects the given key to be present in the given map otherwise returns error. Second one expects the given interface to be convertible into string otherwise returns error. Third one is for more general purposes. It only lifts bool to error.

func expectKey(m map[string]interface{}, k string) (interface{}, error) {
    if v, ex := m[k]; ex {
        return v, nil
    } else {
        return nil, fmt.Errorf("key %s not exists in map %v", k, m)
    }
}
func expectToBeString(i interface{}) (string, error) {
    if s, success := i.(string); success {
        return s, nil
    } else {
        return "", fmt.Errorf("conversion to string fail %v", i)
    }
}
func expectTrue(i interface{}, b bool) (interface{}, error) {
    if b {
        return i, nil
    } else {
        return nil, fmt.Errorf("...")
    }
}

I guess the syntax could be like deriveExpectKey(map[A]B, A) (B,error) deriveExpectConversion(interface{}) (A, error) deriveExpectTrue(A, bool) (A, error) thank you.

awalterschulze commented 5 years ago

So currently we have:

deriveCompose(func(A) (B, error), func(B) (C, error)) func(A) (C, error)

which emulates compose for Either Haskell.

So we can also emulate compose for Maybe in Haskell:

deriveCompose(f func(A) (B, bool), g func(B) (C, bool)) func(A) (C, bool) {
    return func(a A) (C, bool) {
         b, ok := f(a)
         if !ok {
              return <zero>, false
         }
         return g(b)
    }
}

But that only addresses your subject line and not any of your deriveExpect functions that your were actually after, right?

ingun37 commented 5 years ago

Sorry for being not clear What I basically want is a transformation like this

MakeComposable :: (a -> (b, bool)) -> (a -> (b, error))

So I can compose a bool-returning function with traditional error-returning function. Because in Go it's common to use bool and error interchangably when you use either-typed error handling.

awalterschulze commented 5 years ago

This sounds like a great plan. So you want a function for maybeToEither.

I think you would also need to provide a string, to populate the error with:

deriveBoolToError :: string -> (a -> (b, bool)) -> (a -> (b, error))

But I think I prefer providing the error itself:

deriveBoolToError :: error -> (a -> (b, bool)) -> (a -> (b, error))

I think we should also support tuple, as the base use case:

deriveBoolToError :: error -> (() -> (b, bool)) -> (() -> (b, error))

What do you think?

ingun37 commented 5 years ago

It looks perfect😃! I'm sure lot of people would really appreciate it because bool-returning functions are everywhere especially built-in type-conversion and map-indexing

awalterschulze commented 5 years ago

Excellent :) Are you interested in adding this feature to goderive?

I think looking at curry should be a good starting point: https://github.com/awalterschulze/goderive/blob/master/plugin/curry/curry.go The tests are located here: https://github.com/awalterschulze/goderive/blob/master/test/normal/curry_test.go If you want to contribute an example, this is explained here: https://github.com/awalterschulze/goderive/blob/master/Contributing.md#contributing-examples

If you find you need more instructions on your journey, adding more docs is always appreciated. I have to admit, that I have been a bit too lazy

ingun37 commented 5 years ago

Sure I'll definetely check them out and see what I can do

awalterschulze commented 5 years ago

I think this task is complete, but let me know if I am wrong.