Zastanawiam się nad udostępnieniem funkcji curry i variadic w funkcjonalnym języku programowania z dynamicznym pisaniem, ale zastanawiam się, czy jest to możliwe, czy nie.
Oto kilka pseudokodów:
sum = if @args.empty then 0 else @args.head + sum @args.tail
który rzekomo ma podsumować wszystkie argumenty. Następnie, jeśli sum
sama traktowana jest liczba, wynik jest następujący 0
. na przykład,
sum + 1
jest równe 1, przy założeniu, że +
może działać tylko na liczbach. Jednak nawet sum == 0
jest prawdą, sum
nadal zachowuje swoją wartość i właściwość funkcjonalną bez względu na liczbę podanych argumentów (stąd „częściowo zastosowane” i „variadyczne” w tym samym czasie), na przykład, jeśli zadeklaruję
g = sum 1 2 3
następnie g
jest równa 6
, jednak nadal możemy dodatkowo zastosować g
. Na przykład g 4 5 == 15
prawda. W tym przypadku nie możemy zastąpić obiektu g
literałem 6
, ponieważ chociaż dają taką samą wartość, gdy są traktowane jako liczby całkowite, zawierają w sobie różne kody.
Jeśli ten projekt zostanie użyty w prawdziwym języku programowania, czy spowoduje jakieś zamieszanie lub dwuznaczność?
źródło
sum
jest0
bez argumentu i rekurencyjnie wywołuje się z argumentem.reduce
?args
:empty
,head
, itail
. Są to wszystkie funkcje listy, co sugeruje, że być może łatwiejszą i prostszą rzeczą byłoby skorzystanie z listy zawierającej różne warianty. (Więcsum [1, 2, 3]
zamiastsum 1 2 3
)Odpowiedzi:
Jak można wdrożyć varargs? Potrzebujemy mechanizmu sygnalizującego koniec listy argumentów. To może być albo
Oba te mechanizmy mogą być używane w kontekście curry do implementacji varargs, ale prawidłowe pisanie staje się poważnym problemem. Załóżmy, że mamy do czynienia z funkcją
sum: ...int -> int
, z wyjątkiem tego, że ta funkcja korzysta z curry (więc faktycznie mamy typ bardziej podobnysum: int -> ... -> int -> int
, z tym wyjątkiem, że nie znamy liczby argumentów).Przypadek: wartość terminatora: Niech
end
będzie specjalnym terminatorem iT
będzie typemsum
. Teraz wiemy, że stosuje się doend
zwrotów funkcyjnych:sum: end -> int
i że stosowane do int możemy uzyskać inną sumę podobnego do funkcji:sum: int -> T
. DlategoT
jest sumą tych typów:T = (end -> int) | (int -> T)
. PodstawiającT
otrzymujemy różne możliwe typy, takie jakend -> int
,int -> end -> int
,int -> int -> end -> int
, itd. Jednak większość systemów typu nie pomieścić takie typy.Przypadek: długość jawna: pierwszym argumentem funkcji vararg jest liczba varargs. Więc
sum 0 : int
,sum 1 : int -> int
,sum 3 : int -> int -> int -> int
itd. To jest obsługiwana w niektórych systemach typu i jest przykładem pisania zależnej . Faktycznie, liczba argumentów byłby parametr typu, a nie regularny parametr - to nie ma sensu na liczbę operandów funkcji zależy od wartości wykonawczego,s = ((sum (floor (rand 3))) 1) 2
jest oczywiście źle wpisane: ten ma wartość albos = ((sum 0) 1) 2 = (0 1) 2
,s = ((sum 1) 1) 2 = 1 2
albos = ((sum 2) 1) 2 = 3
.W praktyce nie należy stosować żadnej z tych technik, ponieważ są one podatne na błędy i nie mają (znaczącego) typu w typowych systemach. Zamiast tego, po prostu przekazać listę wartości jako jeden paramter:
sum: [int] -> int
.Tak, obiekt może pojawiać się zarówno jako funkcja, jak i wartość, np. W systemie pisma z przymusami. Niech
sum
będzie aSumObj
, który ma dwa przymusu:coerce: SumObj -> int -> SumObj
pozwalasum
na użycie jako funkcji, orazcoerce: SumObj -> int
pozwala nam wyodrębnić wynik.Technicznie rzecz biorąc, jest to wariant powyższego przypadku wartości terminatora
T = SumObj
,coerce
który jest rozpakowaniem dla tego typu. W wielu językach obiektowych jest to trywialnie możliwe do wykonania przy przeciążeniu operatora, np. C ++:źródło
..., force=False)
aby wymusić zastosowanie funkcji początkowej.curryList : ([a] -> b) -> [a] -> [a] -> b, curryList f xs ys = f (xs ++ ys)
.Możesz spojrzeć na tę implementację printf w Haskell , wraz z opisem tego, jak to działa . Na drugiej stronie znajduje się link do artykułu Olega Kiselyova na temat robienia tego rodzaju rzeczy, które również warto przeczytać. W rzeczywistości, jeśli projektujesz funkcjonalny język, strona internetowa Olega powinna być prawdopodobnie obowiązkowa.
Moim zdaniem te podejścia są trochę hack, ale pokazują, że jest to możliwe. Jeśli jednak Twój język umożliwia pisanie w pełni zależne, jest to o wiele prostsze. Funkcja wariadyczna sumująca argumenty liczb całkowitych mogłaby wyglądać mniej więcej tak:
Abstrakcja do zdefiniowania typu rekurencyjnego bez konieczności nadawania mu wyraźnej nazwy może ułatwić pisanie takich funkcji.
Edycja: oczywiście, po prostu ponownie przeczytałem pytanie i powiedziałeś dynamicznie pisany język, w którym to momencie oczywiście mechanika typów nie jest tak naprawdę istotna, dlatego odpowiedź @ amon prawdopodobnie zawiera wszystko, czego potrzebujesz. No cóż, zostawię to tutaj na wypadek, gdyby ktoś się z tym spotkał, zastanawiając się, jak to zrobić w języku statycznym ...
źródło
Oto wersja do curryowania różnych funkcji w Pythonie, która wykorzystuje podejście „terminatora” @amona, wykorzystując opcjonalne argumenty Pythona:
Zwracana funkcja
f
zbiera argumenty przekazywane jej w kolejnych wywołaniach w tablicy powiązanej z zewnętrznym zasięgiem. Tylko wtedy, gdyforce
argument jest prawdziwy, wywoływana jest funkcja oryginalna ze wszystkimi zebranymi do tej pory argumentami.Zastrzeżenia do tej implementacji polegają na tym, że zawsze musisz przekazać pierwszy argument, aby
f
nie można było utworzyć „thunk”, funkcji, w której wszystkie argumenty są powiązane i można je wywoływać tylko z pustą listą argumentów (ale myślę, że jest to zgodne z typowa realizacja curry).Kolejnym zastrzeżeniem jest to, że po podaniu niewłaściwego argumentu (np. Niewłaściwego typu) należy ponownie zakryć pierwotną funkcję. Nie ma innego sposobu na zresetowanie tablicy wewnętrznej, odbywa się to tylko po pomyślnym wykonaniu funkcji curry.
Nie wiem, czy twoje uproszczone pytanie „czy obiekt może być jednocześnie funkcją i wartością niefunkcjonalną?”, Może zostać zaimplementowane w Pythonie, ponieważ odwołanie do funkcji bez nawiasów odnosi się do wewnętrznego obiektu funkcji . Nie wiem, czy można to wygiąć, aby zwrócić dowolną wartość.
Prawdopodobnie byłoby to łatwe w Lisp, ponieważ symbole Lisp mogą mieć jednocześnie wartość i wartość funkcji; wartość funkcji wybiera się po prostu, gdy symbol pojawia się w pozycji funkcji (jako pierwszy element na liście).
źródło