Czy istnieją języki programowania, które pozwalają ustawić arytmetykę na typach?


9

Z ciekawości, czy istnieją języki, które pozwalają ci ustawiać arytmetykę typów, aby tworzyć nowe typy? Coś jak:

interface A {
  void a();
  void b();
}

interface B {
  void b();
  void c();
}

interface C = A & B; // has b()
interface D = A | B; // has a(), b() and c()
interface E = (A & B) ^ B; // has c()

Wiem, że w niektórych językach można wyrażać te idee (tj. Java ma List<Comparable & Serializable>na celu połączenie interfejsów), ale nigdy nie słyszałem o języku, który obsługuje arytmetykę typów. Dzięki!


7
Jak przydatny byłby taki mechanizm?
Robert Harvey

4
Wzorzec, który często widziałem, to interfejs, który rozszerza dwa inne interfejsy i niczego nie dodaje (tj. CanWriteAndCompare extends Serializable, Comparable {}) I zastanawiałem się, jak to uogólnić.
Haldean Brown,

2
Ponadto spotkałem się dzisiaj z przypadkiem, w którym mam metodę, która może przyjąć wartość Aa lub a B, z dwiema implementacjami, które wyglądają dokładnie tak samo. W metodzie nazywam metodę polimorficzną, która może przyjąć wartość Aa lub a B, więc implementacje są takie same, ale ponieważ muszę wziąć dwa różne typy, potrzebuję dwóch implementacji. Byłoby łatwiej, gdybym mógł myMethod(A | B aOrB).
Haldean Brown,


1
Oroperacja może być emulowana przez wielokrotne dziedziczenie.
użytkownik

Odpowiedzi:


4

Tangent ( specyfikacja 0.3 ) używa czegoś podobnego do tego. (zrzeczenie się: to mój mały projekt badawczy)

Obecnie withdziała jako operator modelowania dziedziczenia przez związki, chociaż pragmatyzm sprawił, że nie jest on przemienny. Po wprowadzeniu implementacji metod ścisłe połączenie metod o tej samej nazwie często nie jest tym, czego chcesz, i i tak nie można tego zrobić właściwie.

intersectobsługiwane jest wnioskowanie o typach modeli dla czegoś takiego, foo(T,T)gdzie parametry są różne.

Uzupełnienia były interesujące, ale doprowadziły do ​​częściowych typów, które wydawały się nie tak przydatne i / lub kłopotliwe do prawidłowego włączenia - więc nie zostały uwzględnione.

Wiem, że natknąłem się na kilka innych języków badawczych, które miały coś podobnego, ale w tej chwili ich nie pamiętam. Głównym problemem jest to, że rzeczy nie są tak naprawdę przydatne bez strukturalnego pisania, co samo w sobie nie jest zbyt popularne. Drugim jest to, że potrzebujesz jakiegoś rodzaju (rodzaju typów) do przechowywania skonstruowanego typu, w przeciwnym razie jest to po prostu skrót dla czegoś, co nie jest szczególnie idiomatyczne bez tej możliwości. A to znacznie mniej powszechne niż nawet pisanie strukturalne.

Jest tendencyjny i niewiele, ale jest.


5

Tak, Cejlon jest językiem z typami połączeń i skrzyżowań ad hoc, jak opisano w tym rozdziale z wycieczki po Cejlonie:

http://ceylon-lang.org/documentation/1.0/tour/types/

To niesamowita liczba fajnych idiomów, które z tego czerpiesz. Oto jeden interesujący przykład, który niedawno napisałem na blogu . A oto krótka prezentacja, w której szybko przeglądam kilka prostych idiomów .

Co więcej, typy unii / przecięcia są „brakującym ogniwem”, które sprawia, że ​​wnioskowanie argumentów typu ogólnego naprawdę działa poprawnie na Cejlonie, w przeciwieństwie do innych języków, które łączą podtyp i polimorfizm parametryczny.

Zauważ, że istnieją ograniczenia tego rodzaju „arytmetyki typu”, jak to opisałeś. Na przykład nie można mieć ustawionego operatora dopełniania na poziomie typu, przynajmniej nie bez wprowadzenia nierozstrzygalności.

HTH


3

Scala obsługuje go częściowo (przecięcia, ale nie związki), a każdy język z podtypami strukturalnymi (myślę, że OCaml jest przykładem) lub system typów wystarczająco mocny, aby go naśladować (Haskell jest klasycznym), będzie miał pełne „typy-jako-zestawy "możliwości, przynajmniej w obrębie fragmentu systemu typów, który akceptuje takie rzeczy (istotne, gdy jest emulowany ala HList / OOHaskell).

Ponieważ nie znam zbyt dobrze OCaml, podam część twojego przykładu, która działa w Scali:

trait A {
  def a(): Unit
  def b(): Unit
}

abstract class B { // just to prove it works with both traits and classes
  def b(): Unit
  def c(): Unit
}

type C = A with B
type D = { def b(): Unit } // not an exact translation, because unions aren't directly available
// type `E` is unrepresentable

Wersja dla Haskell zależałaby od używanego systemu nagrań i prawdopodobnie byłaby nieco niezręczna, ponieważ byłaby emulowana, a nie natywnie obsługiwana.

O ile mi wiadomo, Ceylon ma wbudowane w język zarówno typy przecinania się, jak i unii, więc można przypuszczać, że można je zakodować na poziomie typu „xor”.


1

Java obsługuje skrzyżowania typów interfejsów w niektórych kontekstach, choć nie jest to tak dalece, jak mogę stwierdzić, pozwalając na tworzenie zmiennych typów skrzyżowań. Rodzaje skrzyżowań mogą wchodzić w grę, na przykład podczas korzystania z ? :operatora. Jeśli drugi i trzeci operand tego operatora są niepowiązanymi interfejsami, które dziedziczą po nakładających się zestawach interfejsów, wynikiem operatora będzie zestaw interfejsów, które są wspólne dla obu.


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.