Friday, February 7, 2014

Взаимодействие F# с Windows Forms и WPF

Здравствуйте, уважаемые читатели моего блога. Сегодня мы поговорим о взаимодействии F#-приложения с Windows Forms и WPF. В последнее время я активно занимаюсь изучением F# и предлагаю вам окунуться вместе со мной в возможности этого языка. Как говорится, меньше слов – больше дела. Для начала создадим проект с названием TestFSharp для примера. Как это сделать, можно увидеть на рисунке ниже.
Затем нужно удалить точку входа в нашу программу, которая по умолчанию будет у Вас создана.
[<EntryPoint>]
let main argv =
    printfn "%A" argv
    0 // return an integer exit code
Эта точка входа обозначает, что Ваше приложение будет запущено на Console Application. Нам же нужно будет запустить наше приложение как приложение Windows Application. Для этого переходим в окно настроек нашего проекта и изменяем запускаемый проект с на Console Application на Windows Application, как показано на рисунке ниже.
А сейчас немножко "поколдуем" над нашим кодом. Для того, чтобы наши контролы заработали, нужно добавить ссылки на System.Windows.Forms и
System.Drawing.
После того, как мы всё сделали, можно спокойно перейти к реализации. В первую очередь необходимо указать пространства имен, которые мы будем использовать. Для этого добавим в наш проект таки строки:
open System
open System.Windows.Forms
open System.Drawing
Мы говорим нашей исполняющей среде, что будем использовать контролы и классы с данного пространства имен. Давайте для примера создадим простенькую форму, в которую добавим кнопочку, при нажатии на которую выведем сообщение через MessageBox.Show(). Полный исходный код приведен ниже.
open System
open System.Windows.Forms
open System.Drawing

let form = new Form(Text = "F# Program")
let button = new Button(Text = "Button Click")
button.Location <- new Point( 100, 100)
form.Controls.Add(button)

button.Click.Add(fun evArgs -> MessageBox.Show("Click me") |> ignore)

Application.Run(form)
С помощью строк
let form = new Form(Text = "F# Program")
let button = new Button(Text = "Button Click")
создаем форму, затем создаем кнопку и добавляем ее на форму. Затем чтобы наш контрол не выводился с верхнего левого края, зададим ему отступ справа и слева на 100 пикселей.
button.Location <- new Point( 100, 100)
Это делается с помощью оператора <-. Этот оператор позволяет изменять только свойства, помеченные как mutable. По умолчанию свойства в F# помечены как неизменяемые, поэтому помните, когда будете писать код, что для свойств нужно указывать явно, что они являются изменяемыми. Строка
form.Controls.Add(button)
добавляет созданную кнопку на форму.
button.Click.Add(fun evArgs -> MessageBox.Show("Click me") |> ignore)
Код, приведенный выше, создает обработчик события нажатия на кнопку. Этот обработчик создан с помощью лямбда-выражения
fun evArgs -> MessageBox.Show("Click me")
Поскольку нам не важен результат, который возвращает метод MessageBox.Show, мы указываем игнорировать данный результат с помощью строки
|> ignore
После нажатия на клавишу F5 видим результат, подобный рисунку ниже.

F# и WPF
Приведем еще один пример использования F# совместно с WPF. Поскольку пример с созданием контролов мы уже видели в предыдущем примере, рассмотрим вариант с использованием XAML-файла. Вы не сможете создать XAML-файл в самой среде Visual Studio 2012, поскольку эти типы файлов недоступны для проекта на языке F#.
Нам необходимо предпринять маленькую хитрость и создать XAML-окно в отдельном редакторе, например, в той же студии или в Expression Blend, а затем добавить его в наш проект. Признаюсь, что для реального проекта это не очень удобно. Но у нас тестовый пример, и сведения о том, как запустить WPF-приложение с F#, думаю, будут интересными. Для работы с нашим WPF-приложением необходимо добавить следующие библиотеки в проект:

WindowsBase.dll
PresentationCore.dll
PresentationFramework.dll
System.Xaml.dll
Пример показан на рисунке ниже.
Простой пример без использования XAML-файла можно написать так:
open System
open System.Windows


let win = new Window(Title = "Hello WPF")
win.Show() |> ignore

#if COMPILED
[<STAThread()>]
do
    let app =  new Application()
    app.Run() |> ignore
#endif
Но похожий пример мы уже создавали, когда написали Windows Forms-приложение в начале статьи. Поэтому пойдем более сложным путем. Выберем созданный уже заранее приготовленный шаблон и добавим его в студию, нажав на проекте правой клавишей мыши и выбрав пункт меню Add->Existing Item…
Выбираем All Files (*.*) и добавляем наше окно, которое хотим отобразить на экране.
Вот какой вид имеет наше окно:
<Window x:Class="WpfApplicationTestFSharpProject.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <StackPanel Orientation="Vertical">
            <TextBox x:Name="FSharpBook" Text="Test"></TextBox>
            <Button x:Name="PressButton" Content="Click Me"></Button>
        </StackPanel>
    </Grid>
</Window>
Нам необходимо для прикрепленного окна MainWindow.xaml установить в окне Properties свойство Build Action в Resources. После того, как мы проделали необходимую работу, посмотрим, как будет выглядеть наше решение.
open System
open System.Windows
open System.Windows.Controls

[<STAThread>]
[<EntryPoint>]
let main(_) =
  let window = Application.LoadComponent(new Uri("MainWindow.xaml", UriKind.Relative)) :?> Window
  let button = window.FindName("PressButton") :?> Button
  let textBox = window.FindName("FSharpBook") :?> TextBox
  textBox.Text <- "Hello F#"
  button.Click.Add(fun _ ->
    textBox.Text <- textBox.Text.ToUpper())

  window.Show() |> ignore

  let application = new Application()
  application.Run()
Для нашей программы укажем точку входа и то, что наша программа должна запускаться в основном потоке. Это делается с помощью строк
[<STAThread>]
[<EntryPoint>]
let main(_) =
Строка _ как параметр функции main указывает на то, что нам не важно, какие параметры будут переданы в функцию, так как функция принимает параметры командной строки, мы можем указать args как аргумент функции. Следующим шагом мы загружаем наше окно с помощью функции
let window = Application.LoadComponent(new Uri("MainWindow.xaml", UriKind.Relative))
и приводим результат, который возвращает нашу функцию к типу Window (Примечание: функция Application.LoadComponent возвращает Object). Операция приведения в F# очень простая и легко запоминается:

:?>

Следующим этапом находим наши контролы: TextBox и Button по имени.
let button = window.FindName("PressButton") :?> Button
let textBox = window.FindName("FSharpBook") :?> TextBox
Для примера изменим текст, который был в TextBox, и напишем новый текст "Hello F#". Как это сделать, посмотрите ниже:
textBox.Text <- "Hello F#"
Для кнопки мы создадим обработчик события с помощью лямбда-выражения по такому же принципу, как делали для примера Windows Forms. Только для данного  примера мы в обработчике изменим текст в TextBox, который нашли выше, на текст верхнего регистра.
button.Click.Add(fun _ ->
textBox.Text <- textBox.Text.ToUpper())
Осталось каких-то "два штриха". Нужно показать наше созданное окно и запустить основное приложение с помощью метода application.Run()

Итоги
В данной статье мы рассмотрели возможности взаимодействия F#-проекта с Windows Forms и WPF-контролами. Надеюсь, Вам понравился экскурс в мир F#. Жду ваши комментарии и с удовольствием на них отвечу.   

Monday, February 3, 2014

Введение в F#

Совсем недавно мне довелось познакомиться с таким уникальным языком разработки, как F#. Его гибкость и возможности поразили меня. Признаюсь, знакомство с F# началось у меня около года назад, но тогда этот язык просто не "зацепил" меня. Наконец я попробовал снова сосредоточить на нем внимание, изменив при этом свой подход к изучению. Основная проблема, которая иногда у меня проявляется при знакомстве с новым языком, кроется в попытке проецировать возможности известного языка разработки на изучаемый. Следует рассматривать язык как уникальную единицу, призванную решать свои задачи, и если пробовать нагружать на него специфику известного языка, станут заметны несоответствия и может сложиться мнение, что изучаемый язык "недоразвит". Проблема кроется зачастую не в языке, а в нас: мы зачастую пытаемся проецировать то, что есть в нашей памяти, на все новое, пытаясь найти аналогии, и это касается не только разработки. Такое поведение можно наблюдать, например, при изучении иностранных языков. 
Отойдем от рассуждений и попробуем беспристрастно взглянуть на этот язык. Мне понравилась книга "Chad Fowler, The Passionate Programmer", советы из которой стараюсь применять каждый день, чтобы повышать свой уровень. Один из таких советов звучит: "Чтобы понять, действительно ли Вы знаете что-либо, попробуйте обучить этому кого-то еще". Нет лучшего способа закрепить свои знания, чем заставить себя ясно сформулировать их кому-то еще. Обычное формулирование чего-либо является популярным средством для "очищения" разума. Общение с куклами или другими неодушевленными предметами как способ решения проблем является довольно известным элементом программистского фольклора (источник). Поэтому давайте попробуем учиться вместе. Надеюсь, что данный язык Вам понравится своими возможностями и мои старания не прошли даром. Для начала создадим новое приложение, как показано на рисунке ниже.
После этого увидим код: 
[<EntryPoint>]
let main argv =
    printfn "%A" argv
    0 // return an integer exit code
Это пример обычного консольного приложения, в котором [<EntryPoint>] – это точка входа в программу.
let main argv =
Указываем, что функция main является точкой в программу, argv – это аргументы этой функции. Как можно увидеть в Intellicence, функция возвращает int.
Функция printfn аналогична функциям из языка C и Python, только в C# она называется иначе. Например, ту же логику, какую делает эта функция, можно проделать на C# функцией string.Format или для консольного приложения передать необходимые параметры в функцию Console.WriteLine.
Попробуем написать простенькую функцию, которая будет суммировать два числа, и одну функцию, которая будет воспроизводить число в квадрате.
open System

let square x = x * x

let add x y =
    x + y

[<EntryPoint>]
let main argv =
    let z = 5
    printfn "Test square function %i" (square 5)
    printfn "An int: %i" z
    printf "Test add function %i" (add 5 7)
    Console.ReadLine() |> ignore
    0 // return an integer exit code
Для паузы в программе я использовал функцию Console.ReadLine(), но поскольку мне не важен результат, который возвращает данная функция, я указал для данной функции игнорировать результат возвращаемого значения.
Console.ReadLine() |> ignore
Открываем пространство имен System для F# с помощью кода
open System
Теперь поближе остановимся на самих функциях
let square x = x * x
Функция square принимает на вход один параметр. Посмотрите на рисунок ниже, чтобы понять, как работают функции в F#.
Соответственно, функция add принимает на вход два аргумента – x, – и возвращает сумму этих аргументов. Если мы переносим результат выполнения функции на новую строку, то у нас должен быть с левой стороны как минимум один пробел, так как F# не будет понимать, что Вы создали функцию.
Рассмотрим, как нам можно создать кастомный тип данных.
type Person = { Name: string; Age: int}

let mass = [|
                {Name = "Test"; Age = 32};
                {Name = "Test2"; Age = 25}
                |]
Выше мы создали тип данных Person, который имеет два члена Name и Age. А ниже  двумерный массив нашего типа Person. К сожалению, типы данных в C# являются неизменяемыми, поэтому мы не можем написать код на подобии такого для изменения некоего значения данного поля.
let person = {Name = "Test"; Age = 32}
person.Name <- "1234567890"
Но мы легко можем создать новый тип Person, в котором обновить нужное значение.
let person = {Name = "Test"; Age = 32}
let partDeux = { person with Name = "Expert F# 2.0" }
Давайте попрактикуемся в создании функций в отдельных модулях. Для этого выберем в Visual Studio создание нового файла, как показано на рисунке ниже.
Затем перенесем наши функции в этот файл.
module Calculation
    let square x = x * x

    let add x y =
        x + y
После этого нужно немного модифицировать код, чтобы можно использовать эти функции.
open System

[<EntryPoint>]
let main argv =
    let z = 5
    printfn "Test square function %i" (Calculation.square 5)
    printfn "An int: %i" z
    printf "Test add function %i" (Calculation.add 5 7)

  
    Console.ReadLine() |> ignore
    0 // return an integer exit code
Но как Вы увидите, Intellicence будет подчеркивать данный код как ошибку. Все дело в том, что порядок следования файлов в языке F# имеет значение. Нам достаточно выделить добавленный нами файл и нажать Move Up, как показано на рисунке ниже.
Вам необходимо переместить добавленный файл Calculation.fs, чтобы он оказался перед файлом Program.fs. После запуска мы можем запустить приложение и увидеть, что оно работает.
Что же делать, если нам нужно несколько модулей, как разделить их, ведь в одном файле они работать не будут. Для этого в F# существует разделение по пространствам имен namespace. Добавим еще один модуль в файл Calculation.fs и назовем его Multiply. Затем те два модуля, которые у нас получились, добавим в пространство имен CalculationOperation.Code. Пример приведен ниже.
namespace CalculationOperation.Code
    module Calculation =
        let square x = x * x

        let add x y =
            x + y
    module Multiply =
        let div x y = x / y

Теперь посмотрим, как использовать то, что мы написали. J
open System
open CalculationOperation.Code

[<EntryPoint>]
let main argv =
    let z = 5
    printfn "Test square function %i" (Calculation.square 5)
    printfn "An int: %i" z
    printf "Test add function %i" (Calculation.add 5 7)
    printf "Test div function %i" (Multiply.div 4 2)
  
    Console.ReadLine() |> ignore
    0 // return an integer exit code

Итоги
В приведенной статье мы рассмотрели простенькие примеры использования возможностей языка F#. Надеюсь, что примеры были несложные и это не отпугнет Вас от дальнейшего изучения данного языка. В следующей статье попробуем создать на F# приложение Windows Forms и WPF. Для первого раза, думаю, будет достаточно, чтобы не отбить у читателей охоту изучать F# в дальнейшем.

Saturday, February 1, 2014

Советы по карьере и навыкам

Перевод статьи Jon Skeet, Career and skills advice


Немного странно даже писать этот пост, но я получаю множество писем с просьбами советов о том, как стать лучше в программировании, как проходить интервью, кем лучше быть: универсалом или специалистом и т.д. Хочу, чтобы сразу было понятно: я не эксперт по профориентации. У меня очень мало оснований того, что эти советы стоящие; они могут и не быть дельными советами для Вас, даже если это хорошие советы в целом. Да, и не стоит ожидать нечто шокирующе-проницательного или оригинального. Вы также могли уже слышать все или часть этих советов.
Итак, с такими странными предостережениями, мои случайные мысли...
Общение, общение, общение
В программном обеспечении все дело в общении, пишете ли Вы проектную документацию, отвечаете на вопросы на Stack Overflow, ведете беседу на конференции пользователей, проходите интервью или просто пишете код. На самом деле, написание кода - это общение двух различных аудиторий одновременно: компьютера (компилятора, интерпретатора и др.) и того, кто будет читать и поддерживать код в будущем.
На самом деле, я вижу улучшение общения одним из основных преимуществ Stack Overflow. Каждый раз, когда вы спрашиваете или отвечаете на вопрос - это возможность практиковать общение с другими разработчиками. Является ли Ваш пост четким, насколько это возможно? Дает ли только правильный объем информации: ничего лишнего, в соответствующем и последовательном виде? Попадает ли в правильный тон с читателем, сдерживая любое разочарование из-за проблемы, с которой Вы столкнулись, или осознанных недостатков в тех, кому Вы отвечаете?
Stack Overflow является лишь одним из путей улучшения коммуникативных навыков. Существует много других:
  • Написать блог, даже если Вы думаете, что никто не станет читать. Вы можете быть удивлены. Если Вы тратите время на написание о том, что находите интересным, очень вероятно, что кто-то еще заинтересуется этим. Если Вы вполне этим увлечены, подумайте о написании книги, - сейчас есть много вариантов для публикации, от традиционных издателей до онлайн-подходов.
  • Найти возможность для создания презентаций, будь то в пользовательских группах, на конференциях или на работе. Понимаю, что не всем нравится представлять что-то большим группам людей, даже если убеждаюсь, что настроением трудно проникаться. (Кто бы не хотел бы быть в центре внимания? Я что, такой мелкий?
  • Гордитесь своим общением в коде. Уделите больше времени документации и комментариям, чем обычно; старайтесь думать о том, что читая код, кто-то, возможно, пытается что-то выяснить. (Кроме того, думайте о том, что они должны знать об этом, даже если они не в курсе, что им нужно было это знать.)
  • Много читайте и размышляйте о прочитанном. В то время, как мы привыкли комментировать качество и домыслы в сообщениях блога, мы не уделяем внимание сознательному обсуждению качества другой письменной работы. В следующий раз, читая документацию по какой-то библиотеке, которую используете, найдите время, чтобы спросить себя, насколько она эффективна и что способствует повышению качества (или влечет за собой отсутствие такового).
    Вы также можете убедиться, что улучшения Вашего общения также улучшает Ваши мыслительные процессы. Ясность идей и четкость выражений часто идут рука об руку.
Кодирование и отражение
Написав код, просмотрите его - в идеале в значительных промежутках времени. Код, который выглядит "круто" сегодня, может показаться незрелым шесть месяцев спустя.
Есть, конечно, различные виды кода, написанные под разными ограничениями. Вы можете обнаружить, что если Вы уже профессиональный инженер-программист, Вам не дают достаточно времени для критической оценки, но у Вас может быть преимущество в отзывах о коде от ваших коллег. (Отзывы о коде часто могут расцениваться как рутинная работа, но при подходе к ним в правильном расположении духа можно много узнать.) Если Вы сотрудничаете в проекте с открытым исходным кодом (open source project) - или, возможно, создаете такой проект с нуля, то можете убедиться, что у Вас есть больше возможностей, чтобы попробовать несколько подходов и оглянуться на код, написанный ранее
Чтение чужого кода может быть также полезным, хотя я считаю, что это один из тех видов деятельности, о которых больше сказано, чем на самом деле сделано.
Специалист или универсал?
Эрик Липперт написал о том, как ему дали совет, чтобы выбрать тему и стать по ней экспертом мирового уровня, и я думаю, что в этом заложен большой смысл. С другой стороны, Вы не хотите быть настолько ограниченным узкой областью знаний, чтобы развиваться в более широком мире. Здесь подводится баланс: "я универсал" с точки зрения наличия относительно малого количества навыков в конкретных областях дела, но я "специалист" с точки зрения языка: значительного объема отведенного времени на изучение C# и Java. Также я очень ограничен в плане того, в каких аспектах программного обеспечения я компетентен: я хотел бы думать, что я имею достаточную базу по разработке библиотек или серверного кода, но мой опыт разработки пользовательского интерфейса (будь то Интернет или клиентское приложение) сильно ограничен. Я также знаю меньше языков, чем хотел бы знать.
Вы должны решать то, что предпочтительно для Вас, - но это стоит делать сознательно, а не просто плыть по течению. Конечно, принимать продуманные решения - не то же самое, что выполнять их: несколько месяцев назад я решил, что было бы интересно сосредоточиться детально на разработке приложений на некоторое время (WPF , Windows Store, Android и IOS через Xamarin), но до сих пор достиг только небольших результатов.
Иногда меня спрашивают, какие технологии хороши с точки зрения трудоустройства. Это может быть очень локальным вопросом, но мой опыт показывает, что это действительно не имеет слишком  большого значения. Если Вы нашли то, во что увлечены, то все, что в этом узнаете, будет полезно в более обычной, но прибыльной среде. Технологии меняются достаточно быстро, так что Вам почти наверняка придется выучить несколько языков и платформ в Вашей карьере, так что Вы можете сделать увлекательными, по крайней мере, некоторые из этих вариантов.
Интервью
Об интервью было написано много советов людьми с намного большим опытом, чем у меня. На самом деле, я нечасто проходил интервью, хотя сейчас провожу их довольно много. Несколько общих советов:
  • Не делайте слишком много преувеличений в своем резюме. Если Вы утверждаете, что являетесь экспертом в языке, а потом забываете, как написать объявление метода, это оставляет плохое впечатление.
  • Узнайте заранее, что нужно компании и то, из чего, скорее всего, будет состоять интервью. Возможно, Вам будет предложено написать код на доске или в текстовом редакторе? Если это так, было бы неплохо попрактиковаться, - ведь это существенно отличается от написания кода в IDE. Если Вам, скорее всего, зададут вопросы об алгоритмической сложности, а знания с университета ощущаются как давным-давно забытые, следует их немного освежить.
  • Будьте интересными! Это часто сводится к пристрастию в чем-либо. Если единственной мотивацией у Вас является: "Мне скучно на нынешней работе", не упоминая интересных сторон, интервью пройдет неудачно.
  • Внимательно слушайте, о чем Вас спрашивают. Если конкретно просят игнорировать один аспект кодирования (проверку, производительность, какой бы она ни была), то не оправдывайте свой ответ, используя один из этих аспектов. Всеми средствами ссылайтесь на этот аспект, но руководствуйтесь данными Вам уточнениями, определенными как важные. (Баланс в интервью может перемещаться, так как Вас пытаются оценить по нескольким критериям.)
  • Общайтесь ясно, насколько можете, даже пока размышляете. Я люблю, когда кандидаты объясняют свою точку зрения, как они справляются со сложной проблемой. Сразу видно, где ими управляет разум, насколько они интуитивны или методичны, где у них могут быть слабые стороны, - и я могу направлять их намеками. Это может стоить нескольких секунд, чтобы выработать наилучший способ озвучивания своих мыслей. Да, и если Вы пишете на доске, постарайтесь сделать это разборчиво.
Итог
Это полностью субъективное руководство Джона по интересной и прибыльной карьере в сфере разработки программного обеспечения. Если Вы решите действовать по некоторым из этих советов, я искренне надеюсь, что это скажется для Вас хорошо, но не гарантирую этого. Желаю удачи в любом случае!