воскресенье, 16 октября 2011 г.

Опережающее объявление макроса

Кто-нибудь знает как в clojure сделать опережающее определение макроса?

Такой код не скомпилируется,  ошибка - "Unable to resolve symbol: my-inc in this context"

(my-inc 78)
(defmacro my-inc[number] `(+ ~number 1))


А так получишь ошибку в рантайме "Wrong number of args (1) passed to: "

(declare my-inc)
(my-inc 78)
(defmacro my-inc[number] `(+ ~number 1))


Я так понимаю declare приходится писать из за того что компилятор не многопроходный?

пятница, 1 июля 2011 г.

Создание экземпляра класса по имени

Для создания экземпляра класс в clojure можно воспользоваться специальной формой new

(new String)
или более краткой записью '.'

(String.)
Но не всегда этих средств достаточно. Возникает необходимость создания объекта класса, зная только имя целевого класса(например хранящегося в виде строки "String"). В этом случае объект можно создать воспользовавшись методами java-классов java.lang.Class(для классов с конструктором без параметров) и java.lang.reflect.Constructor - для конструкторов с параметрами.
Первый шаг создания объекта по имени класса - это найти полное имя класса, решается с помощью функции ns-resolve. Поиск выполняется в текущем пространстве имен(*ns*)

(ns-resolve *ns* (symbol "String"))
java.lang.String
В случае успеха ns-resolve возвращает экземпляр класса java.lang.Class.

Если конструктор класса с параметрами, необходимо найти конструктор с подходящей сигнатурой(метод getConstructor класса java.lang.reflect.Constructor). И создать новый объект методом newInstance.
Окончательно функция будет выглядеть так:

(defn new-instance [name & args]
  (let [class (ns-resolve *ns* (symbol name))]
    (if (= java.lang.Class (type class))
      (if args
        (let [type-args (into-array java.lang.Class (map #(type %) args))]
          (.newInstance (.getConstructor class type-args) (into-array args)))
        (.newInstance class)))))
Пример использования

user=> (new-instance "String")
""
user=> (new-instance "String" "abc")
"abc"
user=> (type (new-instance "String" "123"))
java.lang.String




суббота, 21 мая 2011 г.

Модификация атрибутов объекта в Clojure

Часто необходимо модифицировать состояние объекта, особенно когда работаешь с java библиотекой, смысл которой сводится к вызову callback'ов и необходимо сохранять промежуточное состояние между вызовами. Например SAX парсер.

Clojure определяет три способа работы с изменяемыми данными.  Это ссылки, агенты и атомы. Все они описаны в статье Алексея Отта (http://alexott.net/ru/clojure/clojure-intro/).
Для моего случая подходит синхронное изменение и заворачивать обращения  к изменяемым переменным в транзакции нет необходимости. Поэтому я буду использовать атомы(atoms).

Для примера опишем класс com.Example. Объявим одновременно пространство имен и класс(com.Example) с помощью макроса ns.

(ns com.Example
  (:gen-class
    :state state
    :init init
    :constructors {[] []}
    :methods [[setAttr [clojure.lang.Keyword int] void],
              [getAttr [clojure.lang.Keyword] int],
              [removeAttr [clojure.lang.Keyword] void]]
    :main false))

:init - имя конструктора
:state - имя атрибута для хранения состояния объекта
:constructors - описание типов параметров конструкторов класса
:methods - описание доступных методов класса, в формате [имя [тип_параметра ...] тип_возвращаемого_значения]
:main - false - не генерировать, автоматически, метод main(первый метод который будет вызван для java программы)

Теперь код объявленных выше методов.

Конструктор:

(defn -init []
[[] (atom {})])

конструктор без параметров. Конструктор класса в clojure должен вернуть вектор с двумя элементами.

[ [superclass-constructor-args] state]

Первый элемент -  еще один вектор, содержит параметры конструктора родительского класса([] - для родительского конструктора без параметров). Второй элемент - значение переменной состояния(имя которой определяется через параметр :state). В нашем случае это хеш(maps). Применение функции atoms делает состояние объекта изменяемым.

После того как state в классе объявлен как изменяемый его можно модифицировать функцией swap!.

Атрибуты объекта будут хранится как ключевые слова(keywords) а значения - это целые числа.
Методы для сохранения и получения значения атрибутов объекта будут такими:


(defn -setAttr [this attr value]
  (swap! (.state this) assoc attr value))

(defn -getAttr [this attr]
  (get @(.state this) attr))

(defn -removeAttr [this attr]
  (swap! (.state this) dissoc attr))


И маленький пример использования
user=> (def ex (com.Example.))
#'user/ex
user=> (.state ex)
#<Atom@724eb043: {}>
user=> ex
#<Example com.Example@30c06258>
user=> (.setAttr ex :a 1)
nil
user=> (.setAttr ex :b 2)
nil
user=> (.setAttr ex :super-key -9)  
nil
user=> (.state ex)
#<Atom@724eb043: {:super-key -9, :b 2, :a 1}>
user=> (.getAttr ex :b)
2
user=> (.removeAttr ex :super-key)
nil
user=> (.state ex)                
#<Atom@724eb043: {:b 2, :a 1}>

четверг, 28 апреля 2011 г.

Запуск одного Activity из другого

Для того что бы в android приложении запустить одно Activity из другого(например для навигации по интерфейсу приложения) нужно воспользоваться механизмом межпроцессной коммуникации.  Для передачи данных между процессами служит объект класса Intent. Для запроса activity, описанного в объекте класса Intent, служат методы startActivity и startActivityForResult.
Существует несколько конструкторов для создания объекта Intent. Если говорить про запуск Activity для решения задач навигации по интерфейсу(собственных интерфейсов), то можно использовать конструктор вида

 public Intent (Context packageContext, Class<?> cls)

с жестким заданием имени класса. Так же для запуска activity понадобится описать в файле AndroidManifest.xml вызываемую activity.

<activity android:name="com.example.MyExampleActivity"
android:label="@string/app_name" />

И запустить activity вызовом метода startActivity.

Код на Java для запроса activity может быть записать так:

this.startActivity(
    new Intent(this, com.example.MyExampleActivity.class)
);

На clojure запуск одной Activity из другой будет таким:

(.startActivity this (Intent. this com.example.MyExampleActivity)

this - текущая activity

понедельник, 18 апреля 2011 г.

Программирование на clojure под android

В первом посте хочу написать о том зачем эти посты вообще нужны и о чем будут последующие.

зачем: учится новому, получать обратную связь, обсуждать, решать проблемы
о чем: о программировании на clojure под android

Началось все с того что меня заинтересовала разработка под Android. Вслед за этим возник естественный вопрос на чем разрабатывать. От "официальных" языков разработки под android(Java, С++) я был далек и большого желания их изучать и применять не было. Но со времен института остался интерес к функциональным языкам. Особенно к Lisp(на котором я решал исключительно студенческие задачи). Поэтому почитав о Lisp и android побольше я нашел для себя такую связку как clojure + android.  
Clojure - функциональный язык программирования, транслируется в Java байт код, имеет глубокую интергацию с ЯП Java. Команда clojure заявляет о поддержке платформы android(точнее dalvik - виртуальной машины andorid). По причинам глубокой интеграции с java и заявлению по поддержке android думаю что clojure станет хорошей альтернативой java на android.