Вопрос: Haskell: функции, которые иногда возвращают функцию


Как вы пишете функцию, которая может вернуть значение или другую функцию?

Например:

Function Foo (x)
    If X = 0 Return "Done" 
    Else Return a Function that calls Foo(x-1)

5


источник


Ответы:


В haskell возвращаемый тип функции может зависеть только от типа ее аргументов и, в случае функций с полиморфными типами возврата, как используется возвращаемое значение. В частности, возвращаемый тип функции не может зависеть от значения аргумента.

Другими словами: вы не можете делать то, что хотите напрямую. В тех случаях, когда вы хотите вернуть один из двух типов, обычно вы можете указать тип Either a b который определяется как data Either a b = Left a | Right b, который позволяет вам вернуть значение типа a завернутый в Left или значение типа b завернутый в Right, Затем вы можете использовать сопоставление образцов для получения значения безопасным типом.

Однако, поскольку в этом случае для b должен быть бесконечным, это не сработает, и для этого вам нужно определить свой собственный тип обертки. Вот так:

data MyResult = Str String | Fun ( () -> MyResult)
foo 0 = Str "done"
foo x = Fun (\ () -> foo (x-1))

foo теперь имеет тип Num a => a -> MyResult, Однако каждый раз, когда вы звоните foo вам нужно сопоставить шаблон, чтобы увидеть, вернулась ли строка Str со строкой внутри или Fun с функцией внутри.

Также обратите внимание, что если вы хотите вернуть функцию, а не значение, чтобы отсрочить выполнение, это не имеет смысла в haskell, потому что оно лениво и вообще не оценивается до их использования.


20



Из взглядов вашего псевдокода, я предполагаю, что вы ожидаете вернуть «нулевую» функцию, то есть такую, которая не принимает аргументов, и вызовет «Foo (x-1)» при вызове.

Если это так, то, как указано в конце ответа sepp2k, в Haskell есть такая необходимость - это то, что происходит по умолчанию. В частности:

foo x = if x == 0 then "Done"
                  else foo(x-1)

делает в точку  this: значение, возвращаемое вызовом, скажем, foo(7) это то, что когда программа нуждается в ее ценности, будет оцениваться foo(6), Рекурсивный вызов не будет оцениваться внутри оценки if выражение.


3



Вам нужно подумать о типах вашей функции: если Foo имеет тип (Int -> t), что такое t? В обоих случаях ему нужно вернуть что-то типа t. Я думаю, что это немного сложно, потому что я не думаю, что t может быть строковым типом или типом функции (->) в той же функции.


1



Я знаю, что это не отвечает на ваш вопрос напрямую, но я думаю, вам нужно расширить свое представление о том, что значит «вернуть функцию». Например, функция:

mean3 :: Float -> Float -> Float -> Float
mean3 x y z = (x + y + z) / 3

Можно считать «взятием трех чисел и возвращением числа». Или это можно рассматривать как «функцию, берущую два числа и возвращающую функцию от числа к числу»:

mean3 :: Float -> Float -> (Float -> Float)

mean1 :: (Float -> Float)
mean1 = mean3 1 2

1



Просто следите за отличным ответом sepp2k. Я думаю, что у вас отсутствует фундаментальная концепция в Haskell - вы всегда возвращаете функцию. Даже «ценность» рода - это функция.

Например, распакуйте ghci и попробуйте:

> :t 5
:: (Num t) => t

Просто функция, которая не принимает входных данных, возвращает значение Num.

> :t "What  is this?"
:: [Char]

Аналогично, только функция, которая не принимает значения, возвращает [Char]

«Но все это просто ценности! Я не убежден!»

Что тогда главное? (Предположим, что вы определили его):

> :t main
:: IO ()

Просто функция, возвращающая экземпляр IO ().


1



{-# LANGUAGE ExistentialQuantification #-}

data MyResult = Str String | forall a. Fun a -- deriving Show

foo 0 = Str "done"
foo x = Fun (\ () -> foo (x-1))

такие работы, но вы не можете получить экзистенциальный тип (methinks), поэтому вам нужно вызвать foo: (\(Main.Str x) -> x) (Main.foo 0),

Если вы знаете, как получить главный модуль в фокусе в ghci, напишите комментарий.


0



Обычно мы пишем это как

foo _ = "Done"

или, бесцельно,

foo = const "Done"

(Если, конечно, мы действительно не хотели _|_ для отрицательных чисел ;-)


0