F # Вопросы качества жизни
Я начал кодирование в F # около 2 месяцев назад.
Мне очень нравится этот язык программирования. Я исхожу из фона С#, и каждый раз, когда мне нужно вернуться на С#, он кажется таким громоздким и раздутым.
Но есть еще вещи, которые, по моему мнению, проблематичны в F #, и это то, к чему относятся мои вопросы:
-
Нет автоматического завершения, как VS имеет для С# правильно? Например. внутри функции, которая принимает параметр aParameter, если я пишу aPara, то автозаполнение не появляется. Есть ли функциональность внутри ВС, которая может решить эту проблему и о которой я не знаю?
-
Отладка утомительна, если не сказать больше. Поскольку F # поддерживает компоновку/цепочку или то, что вы хотите назвать, я обычно стараюсь объединить как можно больше вещей (везде, где это имеет смысл). Пример:
correctedData
|> List.filter (fun (_, r, _) -> r <= 3)
|> Seq.ofList
|> Seq.groupBy (fun (_, r, cti) -> (r,cti))
|> Seq.map (fun ((r,cti),xs) -> (r, cti, Seq.length xs))
|> Seq.toList
И это всего лишь четверть всей моей цепочки. Всякий раз, когда я что-то ввязываю в эти цепи, мне очень сложно отлаживать, где все пошло не так.
Я делаю эту цепочку неправильно (злоупотребляя ею)? С моей точки зрения, ничто промежуточное от этой цепочки имеет смысл существовать атомарно, поэтому нет причин иметь промежуточные ценности. Но из-за этой семантической точки зрения я также теряю способность иметь промежуточные ценности, которые помогают мне отлаживать. Поэтому мне нужно вставить их в код, отладить, а затем удалить их снова. Но это потраченное впустую усилие. Есть ли способ обойти это?
Кроме того, отладка анонимной функции List.map внутри цепочки кажется неудобной и сложной по сравнению с, например, цикл for.
Я уверен, что у меня что-то не хватает, и что мой текущий способ отладки, вероятно, не оптимален - не длинным выстрелом - поэтому любые предложения приветствуются.
Ответы
Ответ 1
1. Нет автоматического завершения, как у VS для С# справа
Для F # автозаполнение. Он не запускается автоматически, когда вы начинаете вводить текст. Если вы находитесь в Visual Studio и введите aPara
, а затем нажмите Ctrl + Space, он должен быть автозаполнен до aParameter
, если он находится в области. Аналогично, вы можете сделать это в области верхнего уровня, чтобы увидеть доступные типы и пространства имен. Автозаполнение также запускается автоматически при вводе .
2. Отказывание утомительно, чтобы сказать наименьшее
Я бы согласился с этим - отладочные конвейеры (особенно с ленивыми последовательностями) сложны. Это немного запутанно, даже когда вы на С#, но С# делает удивительно хорошую работу на этом. Есть два способа справиться с этим:
-
Используйте F # Interactive больше. Сначала я пишу большую часть своего кода в файле F # Script, где вы можете запускать свои частично полные решения и сразу видеть результаты. Для меня это в значительной степени заменяет отладку, потому что к моменту завершения моего кода я знаю, что он работает.
-
Вы можете определить функцию tap
, которая материализует данные в конвейере и позволяет вам видеть, что происходит через трубу. Я не использую это очень много, но я знаю, что некоторым нравится:
let tap data =
let materialized = List.ofSeq data
materialized
Затем вы можете использовать его в своем конвейере:
correctedData
|> List.filter (fun (_, r, _) -> r <= 3)
|> tap
|> Seq.groupBy (fun (_, r, cti) -> (r,cti))
|> tap
|> Seq.map (fun ((r,cti),xs) -> (r, cti, Seq.length xs))
|> Seq.toList
Это добавляет некоторый шум в конвейер, но вы можете удалить его снова, как только закончите с отладкой.
Ответ 2
Вопрос об улучшении опыта отладки с F # имеет много аспектов, поэтому он заслуживает большой статьи. Поэтому я боюсь, что вопрос будет закрыт.
Тем не менее, вот два аккуратных трюка, которые я использую. Я должен отметить, что я также большой поклонник трубопроводного подхода, и поэтому сталкиваюсь с теми же проблемами.
Знайте свои типы.
Наличие значения, пронизывающего цепочку из многих преобразований, может быстро привести к затруднению запоминания точных типов на каждом шаге. Трюк:
value
|> transformation1
|> fun x -> x
|> transformation2
Это позволяет вам:
- см. точный тип
x
во время разработки;
- установить точку останова (поместить курсор в тело функции) и увидеть значение во время отладки;
- Даже если забыли в коде после завершения, это оставляет минимальный след.
Условно вывести значения в консоль.
Сложные лямбды, точки останова могут мало помочь. Вот еще один трюк, связанный с тем, который описан в ответе @Tomas: напишите небольшую функцию, например:
let inline debug x =
#if DEBUG
if System.Console.CapsLock then
printfn "%A" x
// obviously, it must not be necessarily printf;
// it can be System.Diagnostics.Debug.WriteLine()
// or any other logger tool that exists in the project.
#endif
x
Код использования выглядит следующим образом:
value
|> transformation1
|> fun x -> x
|> debug
|> transformation2
Идея такова:
- вы устанавливаете точку останова непосредственно перед вызовом
debug
, как описано выше;
- переключатель Caps Lock on
- и Step Over или просто запустить приложение
Если у вас несколько мест, где debug
вызов сидит, они не испортят вывод.
Ответ 3
В отладке | > проблема трубопроводов - попробуйте иметь персональный стандарт кодирования, в котором у вас не более трех или не более четырех строк в таком конвейере. Когда они получат больше времени, рефакторинг. Это то, что я делаю, и это очень помогает.