Макрос фрагмента экто из отдельного модуля без импорта

Вот о чем подумать ...

  1. Вы хотите сделать или преобразовать?
  2. Вы хотите, чтобы результат был как List<T> или IEnumerable<T>.
  3. Если результатом является IEnumerable<T>, вы хотите, чтобы приведение cast / convert выполнялось лениво (т. е. cast / convert фактически не произойдет до тех пор, пока итератор не достигнет каждого элемента)?

Полезное различие между cast / convert, поскольку оператор литья часто включает в себя создание нового объекта и может считаться преобразованием: реализации «Cast» должны автоматически применять операторы преобразования, определенные для задействованных типов; новый объект может быть или не быть сконструирован . Реализации «Преобразовать» должны позволять задавать делегат System.Converter<TInput,TOutput>.

Потенциальные заголовки методов:

List<TOutput> Cast<TInput,TOutput>(IEnumerable<TInput> input);
List<TOutput> Convert<TInput,TOutput>(IEnumerable<TInput> input, Converter<TInput,TOutput> converter);
IEnumerable<TOutput> Cast<TInput,TOutput>(IEnumerable<TInput> input);
IEnumerable<TOutput> Convert<TInput,TOutput>(IEnumerable<TInput> input, Converter<TInput,TOutput> converter);

Проблемные реализации «Cast» с использованием существующей структуры; предположим, что вы передаете вход List<string>, который хотите преобразовать с любым из предыдущих методов.

//Select can return only a lazy read-only iterator; also fails to use existing explicit cast operator, because such a cast isn't possible in c# for a generic type parameter (so says VS2008)
list.Select<TInput,TOutput>( (TInput x) => (TOutput)x );
//Cast fails, unless TOutput has an explicit conversion operator defined for 'object' to 'TOutput'; this confusion is what lead to this topic in the first place
list.Cast<TOuput>();

Проблемные реализации «Преобразовать»

//Again, the cast to a generic type parameter not possible in c#; also, this requires a List<T> as input instead of just an IEnumerable<T>.
list.ConvertAll<TOutput>( new Converter<TInput,TOuput>( (TInput x) => (TOutput)x ) );
//This would be nice, except reflection is used, and must be used since c# hides the method name for explicit operators "op_Explicit", making it difficult to obtain a delegate any other way.
list.ConvertAll<TOutput>(
    (Converter<TInput,TOutput>)Delegate.CreateDelegate(
        typeof(Converter<TInput,TOutput>),
        typeof(TOutput).GetMethod( "op_Explicit", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public )
    )
);

/ Преобразование методов должно включать определенные явные операторы преобразования или позволить указать делегат преобразования. Спецификация языка C # для операторов преобразования - в частности, отсутствие имени метода - затрудняет получение делегата, за исключением отражения. Альтернативой является инкапсуляция или копирование кода конверсии, что приводит к излишней (несовместимости) сложности вашего кода, так как действительно, возможные / разрешенные преобразования являются неявными в присутствии или отсутствии операторов преобразования и должны быть обрабатывается компилятором. Нам не нужно вручную искать критически названные определения (например, «op_Explicit») соответствующих операторов преобразования с отражением в RUN TIME по соответствующим типам. Кроме того, методы Cast / Convert для конверсий с массовыми / списками с использованием явных операторов преобразования должны действительно быть функцией структуры, а с List.ConvertAll<T> они ... кроме спецификации языка затрудняют получение делегата для операторов преобразования эффективно! !!

0
задан NoDisplayName 19 March 2019 в 16:03
поделиться

1 ответ

Я не знаю об этой функциональности, выходящей из коробки, но вы можете сделать что-то вроде:

defmodule(Macro1, do: defmacro(coalesce(foo), do: quote(do: unquote(foo) + 1)))
defmodule(Macro2, do: defmacro(coalesce(foo), do: quote(do: unquote(foo) - 1)))

Это модули с макросами, конфликтующими именами. Мы должны импортировать их как macro1_coalesce и macro2_coalesce.

defmodule EctoExtention do
  defmacro __using__(opts) do
    Enum.flat_map(opts, fn what ->
      {mod, funs} =
        case what do
          {mod, :*} -> {mod, Module.concat([mod]).__info__(:macros)}
          {mod, funs} -> {mod, funs}
        end

      prefix =
        mod
        |> to_string()
        |> String.downcase()

      mod = Module.concat([mod]) # or :"Elixir.#{mod}"

      [
        quote(do: require(unquote(mod)))
        | Enum.map(funs, fn {fun, arity} ->
            args = for i <- 0..arity, i > 0, do: Macro.var(:"arg_#{i}", nil)

            quote do
              @doc ~s"""
              Macro #{unquote(fun)} imported from module #{unquote(mod)}
              """
              defmacro unquote(:"#{prefix}_#{fun}")(unquote_splicing(args)),
                do: unquote(mod).unquote(fun)(unquote_splicing(args))
            end
          end)
      ]
    end)
  end
end

Теперь давайте определим правила импорта макросов из разных модулей.

defmodule AllMyMacros do
  use EctoExtention, Macro1: [coalesce: 1], Macro2: :*
end

:* означает импорт всех макросов. Давайте проверим это:

defmodule Test do
  import AllMyMacros
  def info, do: Using.__info__(:macros)
  def test, do: {macro1_coalesce(42), macro2_coalesce(42)}
end


IO.inspect(Test.info(), label: "macros")
#⇒ macros: [macro1_coalesce: 1, macro2_coalesce: 1]

IO.inspect(Test.test(), label: "test")
#⇒ test: {43, 41}
0
ответ дан Aleksei Matiushkin 19 March 2019 в 16:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: