Objective-D — альтернатива или дополнение к Objective-C

от автора

Objective-C, по-моему мнение, — не очень удобный язык со множеством проблем. Но мы вынуждены писать приложения под iOS на этом языке. Я разработал другой язык, который генерирует код Objective-C. Поэтому в этом языке вы можете легко использовать классы Objective-C и C функции. Вы также легко можете использовать сгенерированные классы в коде Objective-C. Так что есть возможность вести разработку сразу на двух языках. Я назвал этот язык Objective-D.

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

Фичи

Синтаксис

За основу я взял синтаксис Scala, потому что Scala обладает очень компактным синтаксисом и мне это нравиться. Таким образом декларация класса получилось очень простой:

class Foo(bar : int) {     val baz = 1 //constant     var qux = 0 //mutable variable     def twiceBar = bar * 2 //function } 

Функции

Мне нравятся именованные параметры в Objective-C. Это повышает читаемость кода.

def foo(p1 : int, p2 : int) : int = p1 * p2 def bar = foo(p1 = 1, 2) 

Однако, иногда имена параметров излишни, поэтому в Objective-D их можно опустить.

def foo(bar : int) = bar * 326 val bar = 1 foo(bar = bar) foo(bar) 

Классы-заглушки

Эти классы нужны, чтобы можно было использовать Objective-C классы или C структуры в Objective-D. Они просто описывают методы и поля классов или структур.

stub class XMLElement {     def name : string     def text : string     def parent : XMLElement?     def children : [XMLElement] } 

Обобщенное программирование

Ужасно, что Objective-C не поддерживает дженерики и использует утиную типизацию. В Objective-D я внедрил дженерики и использую статическую типизацию.

class Foo {     var array = MutableArray<Bar>() } 

Вывод типов

Вы можете не указывать тип переменной или возвращаемое значение функции. Это может быть вычислено автоматически.

var i = 0 var i : int = 0 //тоже самое 

Типажи (Traits)

Вы можете отнаследовать класс от одного класса и множества типажей, но типаж — не простой интерфейс. Он может содержать поля и функции. Типаж только не может содержать конструкторы.

trait Foo {     def foo = 1 } trait Bar {     var bar = 0 } class Baz extends Foo with Bar 

Структуры

Вы можете определить структуры и функции внутри структуры. Структуры будут сгенерированы в C структуры, а функции в C функции.

struct Vec2(x : float, y : float) {     def dot(vec2 : Vec2) : float = x*vec2.x + y*vec2.y } 

Перегрузка операторов

В настоящее время это работает только со специальными названиями функций (add, sub, div, mul). Но я планирую реализовать следующее:

struct Vec2(x : float, y : float) {     def +(vec2 : Vec2) : float = Vec2(x + vec2.x, y + vec2.y) } 

Блоки

Продемонстрирую синтаксис блоков на примерах:

def filter(predicate : T -> bool) : [T] val array = [4, 3, 5, 1] array.filter{item : int -> item > 3} // [4, 5] array.filter{item -> item > 3 } // [4, 5] array.filter(_ > 3) // [4, 5] 

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

Строки

Вы можете использовать встроенные выражения и переносы внутри строки. Посмотрите на пример:

val a = 1 val b = 2 val s = "a = $a          b = $b" 

Кортежи

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

val tuple = (1, "foo", "bar") tuple.a == 1 tuple.b == "foo" 

Ленивые значения

Эти значения будут вычислены только в момент первого обращения.

class Foo(bar : int) {     lazy val barSquare = bar * bar } 

Сопоставление с образом

Сопоставление с образом — большая тема в функциональном программировании. Это позволяет создавать более лаконичный и чистый код.

def foo(bar : (int, int)) : int = case(bar) {     (a, 1) -> a     (0, _) -> 1     _ -> 0 } 

Перечисления

Мне нравятся перечисления в Java, поэтому я реализовал что-то похожее Objective-D.

enum Foo(bar : int) {     baz(1)     qux(2) } 

Пакеты

Отсутствии системы пакетов — большая проблема Objective-C, и мне совсем не нравятся префиксы классов.

package com.foo import com.baz.ParticularClass import com.bar._ 

Но префиксы необходимы в Objective-C, так что вы можете определить префикс пакета в Objective-D в пакетном объекте. Этот префикс будет добавлен к сгенерированным классам и файлам. В пакетном объекты вы также можете определить общие импорты для всех классов пакета.

package com object foo {     import com.bar._     val prefix = "FOO" } 

История разработки

Когда я начинал разрабатывать Raildale я использовал Cocos2D и Objective-C. Позже я отказался от Cocos2D и перешел просто на OpenGL, так как я решил продолжить разработку. Если бы я использовал OpenGL с самого начала, я мог бы выбрать C++ вместо Objective-C.

Чере некоторое время после начала разработки я понял, что Objective-C начал меня раздражать. Первой проблемой стало, что там нет перечислений. Я имею ввиду перечесления со связанными свойствами как в Java или чего-нибудь, что могло бы это заменить. Я придумал несколько методов, как решить эту задачу, но все равно требовалось много кода. Я пробовал использовать макросы, но найти хорошее решение у меня так и не получилось.

И я решил разработать генератор кода для перечислений, что является нормально практикой в языко-ориентированном программировании. Это была первая часть Objective-D:

enum RailConnector(x : int, y : int, angle : int) {     left(-1, 0, 0)     bottom(0, -1, 90)     top(0, 1, 270)     right(1, 0, 180) } 

В качестве платформы для разработки генератора я выбирал из трех возможностей: JetBrains MPS, Scala и Haskell. Я выбрал Haskell, потому что там есть отличная библиотека для парсинга(Parsec), и я не пробовал Haskell до этого. Должен сказать Haskell — прекрасный язык.

Я разработал перечисления очень быстро. И тут я подумал, что было бы здорово писать неизменяемые классы в простом лаконичном синтаксисе, и это несложно реализовать. И я разработал генератор для этого случая. Потом я добавил возможность писать простые функции в классах и продолжал добавлять фичи в процессе разработки Raildale. Также я разработал плагин для AppCode, которые подсвечивает синтаксис и позволяет делать некоторые простые рефакторинги. На разработку Objective-D к настоящему моменту я потратил 230 часов.

Как попробовать Objective-D

Я был бы рад, если бы кто-нибудь попробовал Objective-D, я помогу и постараюсь исправить возникающие у вас проблемы. Если кто-то захочет поучаствовать в разработке, буду очень рад.

  1. Скачайте Objective-D и разархивируйте;
  2. Создайте проект в XCode;
  3. Скопируйте папку ObjDLib в ваш проект и добавьте m-files в вашу цель;
  4. Добавьте Build phase в вашу цель (Editor->Add Build Phase->Add Run Script Build Phase) и вызовите ObjD файл в папку bin:
    $OBJD_HOME$/bin/ObjD
  5. Создайте файл с расширением od и напишите туда Objective-D код. Например:
    package test   class Foo(bar : int) 

  6. Соберите проект. h-файл and m-файл будут сгененрированы в той же папке, что и od-файл. Добавьте эти файлы к вашему проекту.

Вы можете также скачать плагин для AppCode.

План развития

  • Генерация в Java — я собираюсь портировать Raildale на Android;
  • Убрать статические функции и поля, полностью заменив их на объекты;
  • Добавить больше проверок, чтобы сделать разработку более безопасной;
  • Значения по-умолчанию для параметров функции.

Ссылки

P.S. Это перевод моей же статьи с английского языка. Оформлял, это не как перевод, так как в правилах сайта сказано, что я не могу вставлять ссылки на свой сайт в статью, если она только не в блоге «Я пиарюсь». Спрашивал о такой ситуации у администрации сайта, ответа на запрос так и не получил. Так что ссылки решил не указывать.

ссылка на оригинал статьи http://habrahabr.ru/post/209464/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *