F# заключает операторы в блок do

У меня есть вопрос относительно соглашений об использовании блоков do в F#. В первую очередь это возникает при работе с классами библиотеки .NET и другим кодом .NET.

Позвольте привести пример.

<сильный>1. С блоком do, обернутым вокруг инструкций:

let drawHelloName (width:int, height:int) name = 
    let bmp = new Bitmap(width, height)
    use fnt = new Font("Arial", 12.0f)
    use gr = Graphics.FromImage(bmp)
    do
        gr.Clear(Color.White)
        gr.DrawString(name, fnt, Brushes.Black, PointF(10.0f, 10.0f))
    bmp

<сильный>2. Без блока do:

let drawHelloName (width:int, height:int) name = 
    let bmp = new Bitmap(width, height)
    use fnt = new Font("Arial", 12.0f)
    use gr = Graphics.FromImage(bmp)
    gr.Clear(Color.White)
    gr.DrawString(name, fnt, Brushes.Black, PointF(10.0f, 10.0f))
    bmp

Сейчас я думаю, что пример 1 понятнее и ближе к сути и стилю F#. Поскольку работать с операторами в функциональном программировании не совсем «естественно», мы явно заключаем операторы в блок do, чтобы показать, что они являются побочными эффектами. Но мне интересно, какова конвенция по этому поводу?


person Overly Excessive    schedule 09.12.2014    source источник
comment
В то время как автономный вызов метода выглядит одинаково в C# и F#, F# утверждает, что такие вызовы возвращают unit, что во многих случаях делает do излишним.   -  person Daniel    schedule 09.12.2014


Ответы (1)


Поскольку работать с операторами в функциональном программировании не совсем «естественно», мы явно заключаем операторы в блок do, чтобы показать, что они являются побочными эффектами.

Я с тобой согласен. Однако, если вы посмотрите на код F# в дикой природе, они, как правило, свободны в этом вопросе. Здесь нет строгой условности, просто следуйте тому, что вы считаете наиболее подходящим для вас.

Другой момент заключается в том, что блоки do создают новые области для значений, время жизни которых мы хотели бы явно контролировать. Например, если вы хотите избавиться от gr раньше и продолжить использовать fnt, ваша первая функция может быть записана в:

let drawHelloName (width:int, height:int) name = 
    let bmp = new Bitmap(width, height)
    use fnt = new Font("Arial", 12.0f)
    do
        use gr = Graphics.FromImage(bmp)
        gr.Clear(Color.White)
        gr.DrawString(name, fnt, Brushes.Black, PointF(10.0f, 10.0f))
    (* Continue to do something with 'fnt' *)
    bmp

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

type T(width, height) =
    let bmp = new Bitmap(width, height)
    use fnt = new Font("Arial", 12.0f)
    use gr = Graphics.FromImage(bmp)
    do
        gr.Clear(Color.White)
        gr.DrawString(name, fnt, Brushes.Black, PointF(10.0f, 10.0f))
person pad    schedule 09.12.2014
comment
Увы, в спецификации F# не определено do block. У нас есть do statements в модулях и первичных конструкторах, последний даже как static do. Поведение do внутри значений или определений функций кажется эквивалентным parenthesized expression или block expression (см. §6.5.1 Выражения в скобках и блочные выражения), но никто не связывает их с побочными эффектами. - person kaefer; 09.12.2014
comment
@kaefer В чем разница? - person Overly Excessive; 09.12.2014
comment
Отличие состоит в утверждении типа unit. Кроме того, type X() = do 1 выдает ошибку компиляции, а module Y = do 1 или let z = do 1 — просто предупреждения. - person kaefer; 10.12.2014