How is the function "transparently complemented" in Haskell?

Situation

I have a function fthat I want to increase using a function g, as a result we get a function with a name h.

Definitions

By "complement" in the general case, I mean: transform either the input (one or more arguments) or the output (return value) of the function f.

By "addition" in a specific case (specific to my current situation) I mean: convert only the output (return value) of the function f, leaving all arguments intact.

By “transparent” in the context of “increase” (both for the general case and for the specific case) I mean: “Connect the implementation gas little as possible with the implementation f.

Specific case

In my current situation, this is what I need to do:

h a b c = g $ f a b c

I am interested in rewriting it to something like this:

h = g . f -- Doesn't type-check.

Since hit gdoes not matter from the point of view which arguments fare taken, they only care about the return value, so it would be a tough connection to mention the arguments in any way. For example, if the number of arguments fchanges in the future, you will also need to change h.

Till

I asked lambdabot on the #haskell: IRC channel @pl h a b c = g $ f a b c, to which I received a response:

h = ((g .) .) . f

, (.) f.

, erisco #haskell http://matt.immute.net/content/pointless-fun, .

, , , :

h = f $. id ~> id ~> id ~> g

, , , , f, f - .

JavaScript, , :

function h () { return g(f.apply(this, arguments)) }

" " Haskell?

, , .

+4
4

, , IncoherentInstances :

{-# LANGUAGE MultiParamTypeClasses, TypeFamilies,
  FlexibleInstances, UndecidableInstances, IncoherentInstances #-}

class Augment a b f h where
   augment :: (a -> b) -> f -> h

instance (a ~ c, h ~ b) => Augment a b c h where
   augment = ($)

instance (Augment a b d h', h ~ (c -> h')) => Augment a b (c -> d) h where
   augment g f = augment g . f

-- Usage
t1 = augment not not
r1 = t1 True

t2 = augment (+1) (+)
r2 = t2 2 3

t3 = augment (+1) foldr
r3 = t3 (+) 0 [2,3]
+3

, , , , .

{-# LANGUAGE TypeFamilies, DefaultSignatures #-}

class Augment a where
  type Result a
  type Result a = a

  type Augmented a r
  type Augmented a r = r

  augment :: (Result a -> r) -> a -> Augmented a r

  default augment :: (a -> r) -> a -> r
  augment g x = g x

instance Augment b => Augment (a -> b) where
  type Result (a -> b) = Result b
  type Augmented (a -> b) r = a -> Augmented b r

  augment g f x = augment g (f x) 

instance Augment Bool
instance Augment Char
instance Augment Integer
instance Augment [a]

-- and so on for every result type of every function you want to augment...

:

> let g n x ys = replicate n x ++ ys
> g 2 'a' "bc"
"aabc"
> let g' = augment length g
> g' 2 'a' "bc"
4
> :t g
g :: Int -> a -> [a] -> [a]
> :t g'
g' :: Int -> a -> [a] -> Int
+5

, - a -> b -> c c, b -> c. , , - , , . , . , HList , . , . .

+3

JavaScript , , . , , .

In a strongly typed language, you need much more information to make it "transparent" to the type of function - for example, dependent types can express this idea, but require that the functions have certain types, and not an arbitrary type of function.

I think I saw a workaround in Haskell that can do this too, but, again, this only works for certain types that capture the arity of the function, not some function.

0
source

Source: https://habr.com/ru/post/1536734/


All Articles