Typy sum - dlaczego w Haskell `show (Int | Double)` różni się od `(show Int) | (pokaż Double) `

9

Dlaczego nie są równoważne?

show $ if someCondition then someInt else some double

i

if someCondition then show someInt else show someDouble

Rozumiem, że jeśli wyodrębnisz if ... elseczęść w pierwszym przykładzie do wyrażenia samodzielnie, nie będziesz mógł reprezentować jego typu za pomocą anonimowego typu sumy, takiego Int | Doublejak coś, co można łatwo zrobić w TypeScript (wspominając o TypeScript, ponieważ jest to langauge, którego często używałem i który obsługuje typy Sum), i musiałbym skorzystać z Eitherdanych, a następnie na podstawie tego zadzwonić show.

Podany przeze mnie przykład jest trywialny, ale dla mnie bardziej sensowne jest myślenie „OK, pokażemy coś i że coś zależy od someCondition”, a nie „OK, jeśli jakiś warunek jest prawdziwy, to pokaż niektóre, w przeciwnym razie pokaż jakieś double”, a także pozwala dla mniejszego duplikowania kodu (tutaj pokaz powtarza się dwa razy, ale może to być również długa aplikacja funkcji i zamiast if ... elseniej można rozważyć> 2 gałęzie)

Moim zdaniem kompilator powinien łatwo sprawdzić, czy każdy z typów tworzących typ sumy (tutaj Int | Double) może być użyty jako parametr do showdziałania i decyduje, czy typy są poprawne, czy nie. Jeszcze lepsze jest to, że showfunkcja zawsze zwraca stringbez względu na typy parametrów, więc kompilator nie musi nosić ze sobą wszystkich możliwych „gałęzi” (czyli wszystkich możliwych typów).

Czy z wyboru taka funkcja nie istnieje? Czy może wdrażanie jest trudniejsze niż myślę?

Mehdi Saffar
źródło
2
An if ... then ... else ...musi mieć ten sam typ w części theni else. W niektórych językach programowania można go postrzegać jako operator trójskładnikowy.
Willem Van Onsem,
1
Zgadzam się w sprawie making all conversions explicit. Na moje pytanie, nie chcę Haskell rzutować IntDo Doublelub vice versa. Właśnie użyłem tych dwóch typów jako przykładu. Można zastąpić każdy Intz aa Doublez bmoim pytaniem, gdzie oba typy wywodzą Show. Rozumiem, że nie ma go anonymous sum typesw Haskell, ale chciałbym wiedzieć, dlaczego tak jest i co powstrzymuje nas od zaprojektowania języka, aby go mieć.
Mehdi Saffar
4
Myślę, że takie typy są nazywane typami związków . Typ sumy jest zasadniczo oznaczonym wariantem typu unii, w którym każda wartość musi nosić lewy / prawy znacznik poza wartością typu wewnętrznego. Oczekuję, że wnioskowanie o typach z typami związków, przy wszystkich cechach Haskell na poziomie typu, jest bardzo trudne do osiągnięcia. Jeśli x :: Int | Booli musimy skompilować show x, nie ma łatwego sposobu, aby dowiedzieć się, którego wskaźnika użyć do wywołania show, w RTS opartym na usuwaniu typów. Prawdopodobnie musielibyśmy zachować pewne informacje na poziomie typu w czasie wykonywania.
chi
1
Brak anonimowych typów sum jest decyzją projektową projektantów Haskell. Nie jestem pewien, jakie korzyści przyniosą na stole, ale widzę, gdzie by to skomplikowały. Domyślam się, że zostały pominięte, ponieważ nie ma tam stosunku kosztów do korzyści. Ale aby być absolutnie pewnym, musisz zapytać oryginalnych projektantów języka i / lub obecnych opiekunów. Nie sądzę, że byłoby to świetne pytanie SO, ponieważ przy projektowaniu języka jest wiele osobistych opinii i gustów.
n. „zaimki” m.
4
(String, Int)nie jest anonimowy. Jest to zwykły typ produktu ze śmieszną składnią. (String | Int)byłoby zupełnie inaczej. Zacznij od pytania, czy (Int|Int)powinno być identyczne Inti dlaczego.
n. „zaimki” m.

Odpowiedzi:

8

Wszystkie części wyrażenia muszą być dobrze napisane. Taki typ if someCondition then someInt else someDoublemusiałby być podobny exists a. Show a => a, ale Haskell nie obsługuje tego rodzaju egzystencjalnej kwantyfikacji.

Aktualizacja: jak chi wskazuje w komentarzu , byłoby to również możliwe, gdyby Haskell obsługiwał typy złączy / skrzyżowań (które nie są takie same jak typy sum / typów), ale niestety nie.

Joseph Sible-Reinstate Monica
źródło
Czy mógłbyś wyjaśnić różnicę między typami połączeń / skrzyżowań a typami sum / produktów? Zawsze myślałem, że są takie same, z wyjątkiem anonimowości?
Mehdi Saffar
1
@MehdiSaffar Anonimowy jak w nieoznakowanym, nie jak w nienazwanym konstruktorze. Innymi słowy, jeśli masz Int ∪ Double, wiedziałbyś, że masz jeden z dwóch, ale nie byłbyś w stanie dopasować wzoru, aby zobaczyć, który z nich, więc możesz robić tylko rzeczy, które byłyby uzasadnione dla obu możliwości.
Joseph Sible-Reinstate Monica
2
Dla jasności TypeScript udostępnia informacje o typie w czasie wykonywania, więc ma typeofoperator, który może zrekompensować brak tagowania i zobaczyć, który typ jest używany. Haskell jest w pełni wymazany, więc jeśli obsługiwałby tę funkcję, to nie byłoby tego równoważne.
Joseph Sible-Reinstate Monica
7

Istnieją typy produktów o lekkiej składni, napisane (,) , w języku Haskell. Można by pomyśleć, że typ sumy z lekką składnią, coś w stylu (Int | String), byłby świetnym pomysłem. Rzeczywistość jest bardziej skomplikowana. Zobaczmy, dlaczego (zabieram trochę swobód Num, nie są one ważne).

if someCondition then 42 else "helloWorld"

Jeśli to powinno zwrócić wartość typu typu (Int | String), to co powinien zwrócić następujący?

if someCondition then 42 else 0

(Int | Int) oczywiście, ale jeśli to różni się od zwykłego Int mamy poważne kłopoty. (Int | Int)Powinien więc być identyczny jak zwykły Int.

Od razu widać, że jest to nie tylko lekka składnia dla typów sum, ale całkowicie nowa funkcja języka. Inny rodzaj systemu typów, jeśli chcesz. Powinniśmy mieć?

Spójrzmy na tę funkcję.

mysteryType x a b = if x then a else b

Teraz jaki typ mysteryType ma? Oczywiście

mysteryType :: Bool -> a -> b -> (a|b)

dobrze? Co teraz, jeśli aib są tego samego typu?

let x = mysteryType True 42 0

Powinno to być jasne, Intjak wcześniej uzgodniliśmy. Teraz mysteryTypeczasami zwraca anonimowy typ sumy, a czasem nie, w zależności od przekazywanych argumentów. Jak pasowałbyś do takiego wyrażenia? Co na ziemi możesz z tym zrobić? Z wyjątkiem trywialnych rzeczy, takich jak „pokaż” (lub dowolnych metod innych klas typów, których byłby to przypadek), nie wiele. Chyba że dodasz do języka informacje o typie wykonawczym, to znaczy, że typeofjest ono dostępne - i to sprawi, że Haskell będzie zupełnie innym językiem.

Więc tak. Dlaczego Haskell nie jest TypeScript? Ponieważ nie potrzebujemy innego TypeScript. Jeśli chcesz TypeScript, wiesz, gdzie go znaleźć.

n. „zaimki” m.
źródło
1
@MichaWiedenmann Szczerze mówiąc nie wiem, czy „odpowiednie” to właściwe słowo. Myślę, że jest to pomocne w taki czy inny sposób. Tak, zatrzymałem się na chwilę i zastanawiam się, czy to uwzględnić. Ale czasami się mylę.
n. „zaimki” m.
Nie ma problemu, to twój post, decydujesz.
Micha Wiedenmann
Jakie są twoje zaimki?
dfeuer