2013-02-10 1 views
20

В таких языках, как SML, Erlang и в Buch других мы можем определить функции, как это:Есть ли какие-либо фундаментальные ограничения, которые останавливают Scala от реализации сопоставления шаблонов по функциям?

fun reverse [] = [] 
| reverse x :: xs = reverse xs @ [x]; 

Я знаю, мы можем написать аналог в Scala, как это (и я знаю, есть много недостатков в коде ниже):

def reverse[T](lst: List[T]): List[T] = lst match { 
    case Nil  => Nil 
    case x :: xs => reverse(xs) ++ List(x) 
} 

Но мне интересно, если бы мы могли написать прежний код в Scala, возможно, с desugaring до последнего.

Есть ли какие-либо фундаментальные ограничения для такого синтаксиса, которые будут реализованы в будущем (я имею в виду, действительно фундаментальный - например, способ вывода типа работает в scala или что-то еще, кроме явно синтаксического анализатора)?

UPD
Вот фрагмент того, как это может выглядеть следующим образом:

type T 
def reverse(Nil: List[T]) = Nil 
def reverse(x :: xs: List[T]): List[T] = reverse(xs) ++ List(x) 
+0

Вы не хотите писать это так, так как это довольно неэффективный «обратный». – Ingo

+2

@Ingo это просто пример (и я добавил отказ от достоверности кода), я мог бы написать факториал в качестве примера, и это будет довольно эффективно. Или вы имеете в виду что-то еще? –

+0

Для меня два выглядят почти так же, за исключением того, что Scala требует, чтобы аргументы были аннотированы по типу, что является следствием OOP/Functional cross breed (против Hindley-Milner в SML). Поэтому я не совсем то, что вы хотели бы видеть (кроме изменения синтаксиса Scala, например, 'def' ->' fun', 'Nil' ->' [] ') –

ответ

14

Это действительно зависит от того, что среднее значение фундаментальный.

Если вы действительно просят «если есть техническая что остановит всю работу помешало бы реализовать эту функцию», то я бы сказал, что ответ нет. Вы говорите об обесцвечивании, и вы на правильном пути здесь. Все, что нужно сделать, - это в основном сшить несколько разделов на одну единственную функцию, и это может быть сделано как простой шаг предварительной обработки (для этого требуется только синтаксическое знание, нет необходимости в семантическом знании).Но для этого даже иметь смысл, я бы определил несколько правил:

  • Функция подписи является обязательным (в Haskell на примере, это будет факультативным, но всегда является необязательным ли вы определяете функцию сразу или в нескольких частях). Мы могли бы попытаться договориться о том, чтобы жить без подписи и попытаться извлечь ее из разных частей, но отсутствие информации о типе быстро зашло бы на нас. Более простой аргумент состоит в том, что, если мы хотим попытаться вывести неявную подпись, мы могли бы также сделать это для всех методов. Но правда в том, что есть очень веские причины иметь явные певцы в scala, и я не могу представить, чтобы это изменить.
  • Все части должны быть определены в пределах одного и того же объема. Для начала они должны быть объявлены в том же файле, потому что каждый исходный файл скомпилирован отдельно, и, таким образом, простого препроцессора было бы недостаточно для реализации этой функции. Во-вторых, в конце концов, мы все-таки заканчиваем одним методом, поэтому вполне естественно иметь все части в одном и том же объеме.
  • Перегрузки не представляется возможным для таких методов (в противном случае мы должны были бы повторить подпись для каждой части просто так препроцессор знает, какая часть принадлежит к которой перегружать)
  • частей добавлены (сшитые) сгенерированной match в порядок они объявлены

Так вот как это может выглядеть следующим образом:

def reverse[T](lst: List[T]): List[T] // Exactly like an abstract def (provides the signature) 
// .... some unrelated code here... 
def reverse(Nil) = Nil 
// .... another bit of unrelated code here... 
def reverse(x :: xs) = reverse(xs) ++ List(x) 

, которые могут быть преобразованы в тривиальном:

def reverse[T](list: List[T]): List[T] = lst match { 
    case Nil  => Nil 
    case x :: xs => reverse(xs) ++ List(x) 
} 
// .... some unrelated code here... 
// .... another bit of unrelated code here... 

Легко видеть, что такое преобразование является очень механическим и может быть сделано только манипулируя источника AST (АСТ произведенный слегка модифицированной грамматике, которая принимает эти новые конструкции), и превращая ее в целевой АСТ (AST, созданный стандартной грамматикой scala). Затем мы можем скомпилировать результат как обычно.

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


Если по фундаментальной вы спрашиваете «есть что-нибудь, что бы сделать эту функцию из места», то можно утверждать, что это не чувствовать себя очень Scala. Но, кстати, это не приносит столько же внимания. Автор (ы) Scala фактически склонен к тому, чтобы сделать язык более простым (как в менее встроенных функциях, пытаясь переместить некоторые встроенные функции в библиотеки), и добавление нового синтаксиса, который не является более читаемым, идет вразрез с целью упрощения.

3

Я не знаю SML или Erlang, но я знаю Haskell. Это язык без перегрузки методов. Перегрузка метода в сочетании с таким сопоставлением шаблонов может привести к двусмысленности. Представьте себе следующий код:

def f(x: String) = "String "+x 
def f(x: List[_]) = "List "+x 

Что это значит? Это может означать перегрузку метода, то есть метод определяется во время компиляции. Это также может означать сопоставление образцов. Будет только метод f (x: AnyRef), который будет выполнять сопоставление.

Scala также назвал параметры, которые, вероятно, также были бы разбиты.

Я не думаю, что Scala может предложить более простой синтаксис, чем вы показали в целом. Более простой синтаксис может работать только в некоторых особых случаях.

3

Есть по крайней мере две проблемы:

  1. [ и ] являются зарезервированными символами, поскольку они используются для аргументов типа. Компилятор допускает пробелы вокруг них, поэтому это не вариант.
  2. Другая проблема в том, что = возвращает Unit. Таким образом, выражение после | не вернется любому результату

Ближайшим я мог придумать это (обратите внимание, что очень специализировано к вашему примеру):

// Define a class to hold the values left and right of the | sign 
class |[T, S](val left: T, val right: PartialFunction[T, T]) 

// Create a class that contains the | operator 
class OrAssoc[T](left: T) { 
    def |(right: PartialFunction[T, T]): T | T = new |(left, right) 
} 

// Add the | to any potential target 
implicit def anyToOrAssoc[S](left: S): OrAssoc[S] = new OrAssoc(left) 

object fun { 

    // Use the magic of the update method 
    def update[T, S](choice: T | S): T => T = { arg => 
    if (choice.right.isDefinedAt(arg)) choice.right(arg) 
    else choice.left 
    } 
} 

// Use the above construction to define a new method 
val reverse: List[Int] => List[Int] = 
    fun() = List.empty[Int] | { 
    case x :: xs => reverse(xs) ++ List(x) 
    } 

// Call the method 
reverse(List(3, 2, 1)) 
5

В SML, ваш фрагмент кода буквально просто синтаксический сахар (а «производная форма» в терминологии языка спецификации) для

val rec reverse = fn x => 
    case x of [] => [] 
      | x::xs = reverse xs @ [x] 

, который очень близок к коду Scala вы показываете. Итак, нет никакой «фундаментальной» причины, по которой Scala не может предоставить такой же синтаксис. Основная проблема заключается в необходимости использования Scala для дополнительных аннотаций типа, что делает этот сокращенный синтаксис гораздо менее привлекательным в целом и, вероятно, не стоит того.

Обратите также внимание на то, что конкретный синтаксис, который вы предлагаете, не будет хорошо летать, потому что не существует синтаксически отличать одно случайное определение функции от двух перегруженных функций. Вероятно, вам понадобится альтернативный синтаксис, похожий на SML, с помощью «|».

Смежные вопросы