Статьи

Typesafe API для браузера

Новая функция в Ceylon 1.1, о которой я раньше не писал, — это динамические интерфейсы . Это было то, над чем мы с Энрике работали вместе с Корбин Уселтон, одним из наших студентов GSoC.

Обычно, когда мы взаимодействуем с объектами JavaScript, мы делаем это из dynamic блока, где обычная скрупулезная проверка типов в Цейлоне подавляется. Проблема с этим подходом состоит в том, что, если это API, которым я пользуюсь регулярно, моя IDE не может помочь мне запомнить имена и подписи всех операций API.

Динамические интерфейсы позволяют приписывать статические типы нетипизированному API-интерфейсу JavaScript. Например, мы могли бы написать динамический интерфейс для HTML 5 CanvasRenderingContext2D следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
dynamic CanvasRenderingContext2D {
    shared formal variable String|CanvasGradient|CanvasPattern fillStyle;
    shared formal variable String font;
 
    shared formal void beginPath();
    shared formal void closePath();
 
    shared formal void moveTo(Integer x, Integer y);
    shared formal void lineTo(Integer x, Integer y);
 
    shared formal void fill();
    shared formal void stroke();
 
    shared formal void fillText(String text, Integer x, Integer y, Integer maxWidth=-1);
 
    shared formal void arc(Integer x, Integer y, Integer radius, Float startAngle, Float endAngle, Boolean anticlockwise);
    shared formal void arcTo(Integer x1, Integer y1, Integer x2, Float y2, Integer radius);
 
    shared formal void bezierCurveTo(Integer cp1x, Integer cp1y, Integer cp2x, Float cp2y, Integer x, Integer y);
 
    shared formal void strokeRect(Integer x, Integer y, Integer width, Integer height);
    shared formal void fillRect(Integer x, Integer y, Integer width, Integer height);
    shared formal void clearRect(Integer x, Integer y, Integer width, Integer height);
 
    shared formal CanvasGradient createLinearGradient(Integer x0, Integer y0, Integer x1, Integer y1);
    shared formal CanvasGradient createRadialGradient(Integer x0, Integer y0, Integer r0, Integer x1, Integer y1, Integer r1);
    shared formal CanvasPattern createPattern(dynamic image, String repetition);
 
    //TODO: more operations!!
}
 
dynamic CanvasGradient {
    shared formal void addColorStop(Integer offset, String color);
}
 
dynamic CanvasPattern {
    //todo
}

Теперь, если мы назначим экземпляр JavaScript CanvasRenderingContext2D этому типу интерфейса, нам не нужно находиться внутри dynamic блока при вызове его методов. Вы можете попробовать его в своем браузере, нажав кнопку «ПОПРОБУЙТЕ ОНЛАЙН»!

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
CanvasRenderingContext2D ctx;
dynamic {
    //get the CanvasRenderingContext2D from the
    //canvas element using dynamically typed code
    ctx = ... ;
}
 
//typesafe code, checked at compile time
ctx.fillStyle = "navy";
ctx.fillRect(50, 50, 235, 60);
ctx.beginPath();
ctx.moveTo(100,50);
ctx.lineTo(60,5);
ctx.lineTo(75,75);
ctx.fill();
ctx.fillStyle = "orange";
ctx.font = "40px PT Sans";
ctx.fillText("Hello world!", 60, 95);

Обратите внимание, что нам не нужно присваивать явный тип каждой операции интерфейса. Мы можем оставить некоторые методы или даже просто некоторые параметры метода нетипизированными, объявив их dynamic . Однако такие операции могут вызываться только из dynamic блока.

Предостережение: динамические интерфейсы — удобная фикция. Они могут упростить работу с API в вашей IDE, но во время выполнения Ceylon ничего не может сделать, чтобы гарантировать, что объект, который вы назначаете типу динамического интерфейса, действительно реализует операции, которые вы ему приписали.

Ссылка: Typesafe API для браузера от нашего партнера JCG Гэвина Кинга в блоге команды Ceylon Team .