На каких языках функция абстракция не примитивна - PullRequest
7 голосов
/ 30 марта 2012

В заданном типе функции Haskell (->) он не является конструктором алгебраического типа данных, и его нельзя повторно реализовать, чтобы он был идентичен (->).

Итак, мне интересно, на каких языках я смогу написать свою версию (->)? Как называется это свойство?

UPD Переформулировка вопроса благодаря обсуждению:

Какие языки не имеют -> в качестве примитивного типа?

Почему -> необходим примитив?

Ответы [ 8 ]

12 голосов
/ 30 марта 2012

Я не могу думать ни о каких языках со стрелками как определяемый пользователем тип.Причина в том, что стрелки - типы для функций - запечены в для системы типов, вплоть до простого типа лямбда-исчисления.То, что тип стрелки должен быть фундаментальным для языка, вытекает непосредственно из того факта, что способ формирования функций в лямбда-исчислении - через лямбда-абстракцию (которая на уровне типа вводит стрелки).

Хотя Марцин удачно отмечаетто, что вы можете программировать в стиле без точек, это не меняет сути того, что вы делаете.Наличие языка без типов стрелок в качестве примитивов идет вразрез с наиболее фундаментальными строительными блоками Haskell.(Язык, на который вы ссылаетесь в вопросе.)

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

Тот факт, что вы обеспокоены использованиемиз-за того, что стрелки запечатлены в языке, это может означать, что вы не понимаете, почему они так привязаны к дизайну языка, возможно, пришло время прочитать несколько глав из ссылки Бена Пирса «Типы и языки программирования» .

Редактировать: Вы всегда можете посмотреть на языки, которые не имеют четкого представления о функциях и имеют свою семантику, определенную по отношению к какому-либо другому пути - например, вперед или PostScript - но вВ этих языках вы не определяете индуктивные типы данных так же, как в функциональных языках, таких как Haskell, ML или Coq.Иными словами, на любом языке, в котором вы определяете конструкторы для типов данных, стрелки естественно возникают из конструкторов для этих типов.Но в языках, где вы не определяете индуктивные типы данных обычным способом, вы не получаете типы стрелок так же естественно, потому что язык просто не работает таким образом.

Другое редактирование: я буду придерживаться одногобольше комментариев, так как я думал об этом прошлой ночью.Типы функций (и абстракция функций) составляют основу практически всех языков программирования - по крайней мере, на каком-то уровне, даже если это «под капотом».Однако существуют языки, предназначенные для определения семантики других языков.Хотя это не совсем соответствует тому, о чем вы говорите, PLT Redex является одной из таких систем и используется для определения и отладки семантики языков программирования.Это не очень полезно с точки зрения практиков (если только ваша цель не состоит в том, чтобы создавать новые языки, в этом случае будет довольно полезной), но, возможно, это соответствует тому, что вы хотите.

3 голосов
/ 05 апреля 2012

Статья Филиппа Уодлера Вызов по значению является двойственным к вызову по имени представляет исчисление, в котором абстракция переменной и коваризуемая абстракция являются более примитивными, чем абстракция функции.Предоставлены два определения типов функций в терминах этих примитивов: одно реализует вызов по значению, а другое - по имени.

Вдохновленный работой Вадлера, я реализовал язык (Ambidexer), который обеспечиваетдва конструктора функциональных типов, которые являются синонимами для типов, созданных из примитивов.Один для вызова по значению и один для вызова по имени.Ни двойное исчисление Вадлера, ни Ambidexter не предоставляют пользовательских конструкторов типов.Однако эти примеры показывают, что типы функций не обязательно являются примитивными и что язык, на котором вы можете определить свой собственный (->), возможен.

3 голосов
/ 01 апреля 2012

В Теория типов Мартина-Лёфа , типы функций определяются через индексированные типы продуктов (так называемые types-типы) .

Как правило, тип функций от A до B можно интерпретировать как (возможно, бесконечную) запись, где все поля имеют одинаковый тип B, а имена полей - это точно все элементы A. Когда вам нужно применить функцию f к аргументу x, вы ищите поле в f, соответствующее x.

В статье в Википедии перечислены некоторые языки программирования, основанные на теории типов Мартина-Лёфа. Я не знаком с ними, но предполагаю, что они являются возможным ответом на ваш вопрос.

3 голосов
/ 31 марта 2012

Какие языки не имеют -> как примитивный тип?

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

Но, как красноречиво и превосходно объяснил @Kristopher, функции являются (или, по крайней мере, могут восприниматься как) самыми основными строительными блоками всех вычислений. Следовательно, даже в Java, скажем, есть функции, но они тщательно скрыты от вас.

И, как кто-то упоминал ассемблер, можно утверждать, что машинный язык (большинства современных компьютеров) является приближением модели регистрационной машины. Но как это сделать? С миллионами и миллиардами логических схем, каждая из которых представляет собой материализацию довольно примитивных чистых функций, таких как NOT или NAND, расположенных в определенном физическом порядке (что, очевидно, является способом, которым аппаратные разработчики реализуют композицию функций) , Следовательно, хотя вы можете не видеть функции в машинном коде, они все еще являются основой.

3 голосов
/ 30 марта 2012

Вы имеете в виду мета-циклические оценщики, как в SICP? Будучи в состоянии написать свой собственный DSL? Если вы создадите свой собственный «тип функции», вам придется самостоятельно позаботиться о его «применении».

Например, вы можете создать свою собственную "функцию" в C, например, с помощью справочной таблицы, содержащей указатели на функции, и использовать целые числа в качестве функций. Конечно, для таких «функций» вам придется предоставить собственную функцию «вызова»:

void call( unsigned int function, int data) { 
  lookup_table[function](data);
}

Возможно, вам также понадобятся некоторые средства для создания более сложных функций из примитивных, например, использование массивов целых чисел, чтобы обозначить последовательное выполнение ваших "примитивных функций" 1, 2, 3, ... и в итоге вы придумаете для себя совершенно новый язык.

Я думаю, что ранние ассемблеры не могли создавать вызываемые "макросы" и должны были использовать GOTO.

Вы можете использовать trampolining для имитации вызовов функций. У вас может быть только глобальное хранилище переменных, возможно, с поверхностное связывание . В таком языке «функции» были бы определяемыми, хотя и не примитивного типа.

Таким образом, наличие функций на языке не является необходимым, хотя это удобно .

В Common Lisp defun - это не что иное, как макрос, связывающий имя и вызываемый объект (хотя lambda все еще является встроенным). Изначально в AutoLisp вообще не было специального типа функции, и функции представлялись непосредственно в кавычках списков s -выражений, с первым элементом в arguments list. Вы можете создать свою функцию, используя функции cons и list, непосредственно из символов в AutoLisp:

(setq a (list (cons 'x NIL) '(+ 1 x)))
(a 5)
==> 6

Некоторые языки (например, Python) поддерживают более одного примитивного типа функции, каждый со своим протоколом вызова, а именно, генераторы поддерживают множественный повторный ввод и возврат (даже если синтаксически с использованием одного и того же ключевого слова def). Вы можете легко представить себе язык, который позволил бы вам определять собственный протокол вызова, создавая новые типы функций.

Редактировать: в качестве примера рассмотрим работу с несколькими аргументами в вызове функции, выбор между автоматическим каррированием или автоматическими необязательными аргументами и т. Д. В Common LISP, например, вы можете легко создать себе два разных call макросы для непосредственного представления двух протоколов вызова. Рассмотрим функции, возвращающие множественные значения не через кучу агрегатов (кортежей в Haskell), а непосредственно в назначенные переменные / слоты получателя. Все это разные типы функций.

3 голосов
/ 30 марта 2012

Определение функции обычно примитивно, потому что (а) функции - это то, как программы выполняют свою работу; и (b) лямбда-абстракция такого рода необходима для возможности программирования в точечном стиле (то есть с явными аргументами).

Вероятно, наиболее близким к вам языком, который соответствует вашим критериям, является язык, основанный на чисто точечной модели, которая позволяет вам создавать свой собственный лямбда-оператор. Возможно, вы захотите изучить бессмысленные языки в целом и те, которые основаны на исчислении SKI в частности: http://en.wikipedia.org/wiki/SKI_combinator_calculus

В таком случае у вас все еще есть примитивная функция типов , и вы всегда будете иметь ее, потому что это фундаментальный элемент системы типов. Если вы хотите вообще отойти от этого, вероятно, лучшее, что вы могли бы сделать, - это какая-то система типов, основанная на теоретико-категориальном обобщении функций, так что функции были бы частным случаем другого типа. Смотри http://en.wikipedia.org/wiki/Category_theory.

2 голосов
/ 30 марта 2012

Полагаю, тупой ответ на ваш вопрос код сборки .Это дает вам примитивы даже «более низкого» уровня, чем функции.Вы можете создавать функции в виде макросов, которые используют примитивы register и jump.

Большинство нормальных языков программирования дают вам возможность создавать функции в виде встроенного языка, потому что функции (или «подпрограммы»)Суть хорошего программирования: повторное использование кода.

2 голосов
/ 30 марта 2012

В Scala вы можете смешивать одну из черт Function, например, Set[A] можно использовать как A => Boolean, поскольку она реализует черту Function1[A,Boolean].Другим примером является PartialFunction[A,B], который расширяет обычные функции, предоставляя метод «проверки диапазона» isDefinedAt.

Однако в Scala методы и функции различаются, и нет способа изменить способ работы методов.,Обычно вы не замечаете разницы, так как методы автоматически переносятся в функции.

Таким образом, вы имеете большой контроль над тем, как реализовывать и расширять функции в Scala, но я думаю, что у вас есть реальная «замена» вразум.Я не уверен, что это даже имеет смысл.

Или, может быть, вы ищете языки с некоторым обобщением функций?Тогда Haskell с синтаксисом Arrow будет соответствовать: http://www.haskell.org/arrows/syntax.html

...