Каковы преимущества встроенной неизменности F # перед C #? - PullRequest
24 голосов
/ 03 февраля 2010
  1. Я слышал, что F # имеет встроенную поддержку неизменяемости, но как насчет того, что не может быть воспроизведено в C #? Что вы получаете от неизменяемых данных F #, которые вы не получаете от неизменяемых данных C #?

  2. Также в F # нет способа создать изменяемые данные? Все ли неизменно?

  3. Если вы используете как C #, так и F # в приложении, можете ли вы изменить неизменяемые данные F # в C #? Или вам просто нужно создать новые типы C #, которые используют неизменяемые данные F # и заменяют ссылки, указывающие на эти данные?

Ответы [ 7 ]

24 голосов
/ 03 февраля 2010
  1. Работа F # облегчает работу с неизменяемыми данными, но в этом нет ничего особенного, что нельзя сделать в C #. Он может не обеспечивать аккуратный синтаксис.

  2. F # поддерживает изменяемые данные с ключевым словом mutable. F # не является чисто функциональным языком. Он поддерживает императивные конструкции, такие как циклы, а также функциональные конструкции.

  3. Нет. Неизменяемые типы действительно неизменны. Система типов .NET является общей для обоих языков. Вы также не можете изменить неизменяемые данные из C # (если не считать отражения и взломов памяти, конечно).

17 голосов
/ 03 февраля 2010

Mehrdad уже ответил на все ваши вопросы, но просто уточню # 1 - разница в количестве кода, который вам нужно написать.Например, вот определение и пример использования только для чтения точки в C #:

class Point
{
    private readonly int x, y;

    public int X { get { return x; } }

    public int Y { get { return y; } }

    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}

var p = new Point(1, 2);

Вы можете использовать автоматические свойства, чтобы сделать это немного короче, но тогда неизменность для кода внутри определения класса не применяется, только для своих клиентов.

В F # вы просто напишите:

type Point = { x: int; y: int }
let p = { x = 1; y = 2 }

И если вы хотите, чтобы он был изменяемым:

type Point = { mutable x: int; mutable y: int }
let p = { x = 1; y = 2 }
p.x <- 3
p.y <- 4
12 голосов
/ 03 февраля 2010

Самое большое различие между F # и C # в отношении неизменяемости заключается в том, что F # является неизменяемым по умолчанию.Можно скопировать все неизменяемые конструкции и коллекции в C #, но это требует lot дополнительной работы.В F # вы просто получаете его бесплатно.

F # - это гибридный язык, который поддерживает изменчивость в нескольких формах.Самое простое из ключевого слова mutable

let value1 = 42; // immutable
let mutable value2 = value1; // mutable
value2 <- 13;  

Запрет трюков с отражением: после того, как данные созданы в F #, они неизменны независимо от языка, на котором вы их используете.

5 голосов
/ 03 февраля 2010

Как уже отмечалось, есть несколько важных преимуществ использования неизменяемых данных.Назовите несколько:

  • Распараллеливание - Проще распараллелить программу, которая вычисляет результат и возвращает его в неизменяемой структуре данных.
  • Понимание кода - если вы пишете программу с использованием неизменяемых данных, единственное, что может сделать фрагмент кода, - это вернуть результат - это делает программу более читабельной, потому что вы видите, что функция / метод делает, просто анализируя входные и выходные данные

Технически, вы можете написать одинаковую неизменную структуру данных как на C #, так и на F #.Проблема в том, что F # поощряет вас делать это, в то время как написание истинных неизменяемых структур данных в C # немного болезненно.Это означает, что при написании кода на F # вы, скорее всего, будете писать неизменяемые структуры (потому что это проще!) И будете использовать изменчивость (что возможно и в F #) только тогда, когда вам это действительно нужно (это сложнее!)

Помимо объявления неизменяемых типов по умолчанию, в F # также есть несколько приятных функций, облегчающих работу с ними.Например, при работе с записями:

type MyRect = { X : int; Y : int; Width : int; Height : int }
let r = { X = 0; Y = 0; Width = 200; Height = 100 }

// You'll often need to create clone with one field changed:
let rMoved1 = { X = 123; Y = r.Y; Width = r.Width; Height = r.Height }

// The same thing can be written using 'with' keyword:
let rMoved2 = { r with  X = 123 }

Вы можете написать тоже самое в C #, но вам нужно объявить неизменную структуру данных с помощью метода WithX, который возвращает клон с измененным Xfield, WithY, WithWidth, etc ... Вот неполный пример:

class MyRect {
  readonly int x, y, width, height;

  MyRect WithX(int newX) {
    return new MyRect(newX, y, width, height);
  }
  // etc...
}

Это большая работа без помощи компилятора.

3 голосов
/ 04 февраля 2010

Что вы получаете неизменным F # данные, которые вы не получаете от C # неизменные данные?

F # позволяет легко создавать модифицированные копии неизменяемых данных, это очень утомительно и требует много времени из C #. Попробуйте создать запись в F #:

type Person = { FirstName: string; MiddleName: string; LastName: string }
let me = { FirstName = "Robert"; MiddleName = "Frederick"; LastName = "Pickering" }

Скажем, я хотел изменить поле фамилии, в F # я могу сделать это, скопировав ключевое слово "with":

let me' = { me with LastName = "Townson" }

Если вы хотите сделать это в C #, вам придется создавать методы, которые копируют каждое поле в новую структуру, и есть большая вероятность, что вы перепутаете поля.

Есть и другие подходящие преимущества для коллекций. Например, C # поддерживает неизменяемую коллекцию ReadOnlyCollection. Однако каждый раз, когда вы хотите добавить новый элемент в эту коллекцию, вы должны сделать копию всей коллекции. F # build in list позволяет вам просто добавить новый элемент в конец списка, не создавая его копию, то есть вновь полученный список фактически разделит большую часть старого списка. i.e.:

let myList = [3; 2; 1]
let myList' =  4 :: myList

При создании «myList» весь «myList» сохраняется без изменений, а «4» добавляется в его голову для создания «myList».

3 голосов
/ 03 февраля 2010

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

Я бы увидел главное преимущество неизменяемости по умолчанию в языках функционального программирования, заключающееся в том, как оно влияет на реальное мышление разработчика. Когда по умолчанию используется мутация, программистское мышление ориентируется на решения путем изменения данных. Когда по умолчанию используется неизменность, программистское мышление ориентируется на решения путем преобразования данных.

Чтобы выяснить, почему последнее мышление лучше. Разрабатывая программы путем изменения данных, вы в конечном итоге увязаете в мелких деталях, поэтому вы сосредотачиваетесь на том, как программа работает «в малом». При разработке программ путем преобразования данных вам не нужно беспокоиться о многих мелких деталях (например, о том, как различные изменяющиеся части вашей программы взаимодействуют друг с другом), и вы сосредотачиваетесь на том, как работает программа. большой ». Таким образом, последний подход направляет разработчика к более четкой и понятной «общей картине» работы программы.

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

Изменить, чтобы ответить на комментарий:

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

F # - нечистый функциональный язык программирования, поэтому он поддерживает изменчивость, когда это необходимо. То, что часть вашего штата изменчива, не отменяет преимуществ того, что большая часть вашего штата является неизменной.

В большинстве случаев вы используете другую (чистую) структуру данных, в которой вы обычно используете массив на императивном языке. Например. кортеж, список или дерево (или компоновка set / map поверх дерева; стандартные библиотеки обычно поставляются с реализациями с хорошей временной сложностью).

И если вы считаете, что вам действительно нужен изменяемый массив (по соображениям производительности), он у вас есть, поэтому ничто не мешает вам его использовать.

2 голосов
/ 04 февраля 2010

Что вы получаете от неизменяемых данных F #, которые вы не получаете от неизменяемых данных C #?

Первое преимущество заключается в том, что функция F # не имеет никакого другого эффекта, кроме возврата своего результата. Глобальные данные не изменяются, изменяемые данные не зависят, и поэтому вам гарантировано, что при условии, что вы придерживаетесь неизменных данных, один и тот же вызов каждый раз дает надежный и надежный результат.

Это первое преимущество уже само по себе приятно, потому что оно облегчает понимание вашего кода, но преимущество big вступает в силу, потому что F # функции могут быть безопасно составлены . Программирование по составу функций оказывается невероятно мощным, модульным способом программирования с повторно используемыми деталями. И вы можете создавать действительно мощные части, которые можно соединять различными способами, чтобы вы могли писать больше программ с меньшим количеством кода.

Эти идеи объяснены лучше, чем когда-либо в исторической статье Джона Хьюза Почему функциональное программирование имеет значение . Джон объясняет, как устранение способности мутировать на самом деле добавляет выразительную силу к языку во всем. Прочитайте газету!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...