Thuốc tiên: sử dụng so với nhập khẩu


134

Sự khác biệt giữa usevà là importgì?

sử dụng là một cơ chế đơn giản để sử dụng một mô-đun nhất định vào bối cảnh hiện tại

https://hexdocs.pm/elixir/Kernel.ecialForms.html#import/2

Nhập chức năng và macro từ các mô-đun khác

Có vẻ như một điểm khác biệt là importchúng ta hãy chọn ra các chức năng / macro cụ thể trong khi usemang mọi thứ vào.

Có sự khác biệt nào khác không? Khi nào bạn sẽ sử dụng cái này hơn cái kia?


Tóm tắt nhanh: import Modulemang lại các chức năng được sử dụng trong mô-đun của bạn. use Modulemang lại các chức năng được sử dụng VÀ hiển thị chúng công khai trên mô-đun của bạn
Jered

Câu trả lời:


213

import Modulemang tất cả các Hàm và Macro Modulekhông được đặt tên vào mô-đun của bạn.

require Modulecho phép bạn sử dụng macro Modulenhưng không nhập chúng. (Các hàm Moduleluôn có sẵn được đặt tên.)

use Modulerequiresmô-đun đầu tiên và sau đó gọi __using__macro trênModule .

Hãy xem xét những điều sau đây:

defmodule ModA do
  defmacro __using__(_opts) do
    IO.puts "You are USING ModA"
  end

  def moda() do
    IO.puts "Inside ModA"
  end
end

defmodule ModB do
  use ModA

  def modb() do
    IO.puts "Inside ModB"
    moda()     # <- ModA was not imported, this function doesn't exist
  end
end

Điều này sẽ không được biên dịch vì ModA.moda()chưa được nhập vàoModB .

Sau đây sẽ biên dịch mặc dù:

defmodule ModA do
  defmacro __using__(_opts) do
    IO.puts "You are USING ModA"
    quote do          # <--
      import ModA     # <--
    end               # <--
  end

  def moda() do
    IO.puts "Inside ModA"
  end
end

defmodule ModB do
  use ModA

  def modb() do
    IO.puts "Inside ModB"
    moda()            # <-- all good now
  end
end

Như khi bạn used ModAnó tạo ra một importcâu lệnh được chèn vào ModB.


6
Câu trả lời chính xác! Để biết thêm thông tin: elixir-lang.org/getting-started/alias-require-and-import.html
justin

Tham gia vào Elixir và đến từ thế giới Python Tôi hơi bối rối về các mô-đun là *.excác tệp và các defmodulekhối, và cách bạn kéo mô-đun từ một tệp vào iex REPL
Nick T

2
Cố gắng để hiểu ví dụ / khái niệm. Trong trường hợp cụ thể này, bạn chỉ chứng minh rằng __using__phương thức được thực thi trên use ModA? Có lẽ sẽ có ý nghĩa khi chỉ sử dụng nhập khẩu trong ModBví dụ bạn đã trình bày đúng?
Ryan-Neal Mes

35

useđược dự định để tiêm mã vào mô-đun hiện tại, trong khi importđược sử dụng để nhập các hàm để sử dụng. Bạn có thể xây dựng một usetriển khai tự động nhập các hàm chẳng hạn, như tôi đã làm với Timex khi bạn thêm use Timexvào một mô-đun, hãy xem timex.ex nếu bạn muốn biết ý tôi là gì , đó là một ví dụ rất đơn giản về cách xây dựng một mô-đun có thể là use'd


1
Vì vậy, nó là chính xác để nói uselà tổng quát hơn import? Đó là, chức năng của importmột tập hợp con củause
User314159

1
importvẫn còn cần thiết, như tôi không biết nếu nó chính xác để nói rằng bạn có thể reimplement importvới usemình, nhưng tôi sẽ không ngạc nhiên nếu nó có thể. uselà hoàn toàn mạnh mẽ hơn mặc dù. Bạn có thể làm những việc rất phức tạp với nó, ví dụ, tôi sử dụng rất nhiều usetrong exprotobufdự án của mình mà bạn có thể kiểm tra nếu bạn muốn thấy nó bị đẩy đến giới hạn của nó. Bạn có thể mở rộng các mô-đun bằng mã, thực thi mã tại thời gian biên dịch, thêm chức năng vào mô-đun, v.v ... Về cơ bản, nó kết hợp importvà sức mạnh của macro.
bitwalker 13/2/2015

cảm ơn đã giải thích chi tiết và tham khảo mã. Tôi nghĩ rằng tôi nhận được nó ngay bây giờ. Tôi vẫn chưa quen với Elixir nhưng tôi nghĩ một khi tôi nhìn vào nhiều trường hợp sử dụng hơn, sự khác biệt sẽ rõ ràng.
Người dùng314159

Không có vấn đề gì, một nơi tuyệt vời khác để tìm kiếm sẽ là khung web Phoenix. Chris McCord đã viết một cuốn sách về các macro Elixir và ông sử dụng chúng rất nhiều ở Phoenix (bao gồm use). Hầu như chắc chắn sẽ dễ đọc qua cho người mới bắt đầu hơn exprotobuf, nhưng tôi nghĩ rằng tôi có thể đẩy useđến giới hạn của exprotobufnó để có thể hữu ích khi xem bạn có thể đi được bao xa.
bitwalker

5
usethực sự không làm được gì nhiều, nó chỉ gọi __using__trên module đã chỉ định.
Patrick Oscarity 13/2/2015

25

Xem trang «bí danh, yêu cầu và nhập» từ hướng dẫn bắt đầu chính thức của thuốc tiên:

# Ensure the module is compiled and available (usually for macros)
require Foo

# Import functions from Foo so they can be called without the `Foo.` prefix
import Foo

# Invokes the custom code defined in Foo as an extension point
use Foo

Yêu cầu

Elixir cung cấp các macro như một cơ chế để lập trình meta (viết mã tạo mã).

Macro là các đoạn mã được thực thi và mở rộng tại thời điểm biên dịch. Điều này có nghĩa, để sử dụng macro, chúng tôi cần đảm bảo mô-đun của nó và việc triển khai có sẵn trong quá trình biên dịch. Điều này được thực hiện với requirechỉ thị.

Nói chung, một mô-đun không cần phải được yêu cầu trước khi sử dụng, ngoại trừ nếu chúng tôi muốn sử dụng các macro có sẵn trong mô-đun đó.

Nhập khẩu

Chúng tôi sử dụng importbất cứ khi nào chúng tôi muốn dễ dàng truy cập các chức năng hoặc macro từ các mô-đun khác mà không cần sử dụng tên đủ điều kiện. Chẳng hạn, nếu chúng ta muốn sử dụng duplicate/2chức năng từ Listmô-đun nhiều lần, chúng ta có thể nhập nó:

iex> import List, only: [duplicate: 2]
List
iex> duplicate :ok, 3
[:ok, :ok, :ok]

Trong trường hợp này, chúng tôi chỉ nhập hàm duplicate(với arity 2) từList .

Lưu ý rằng importing một mô-đun tự độngrequire s nó.

Sử dụng

Mặc dù không phải uselà một lệnh , là một macro liên quan chặt chẽ đến requireviệc cho phép bạn sử dụng một mô-đun trong bối cảnh hiện tại. Cácuse vĩ mô thường được sử dụng bởi các nhà phát triển để mang lại chức năng bên ngoài vào phạm vi từ vựng hiện tại, thường mô-đun.

Đằng sau hậu trường, useyêu cầu mô-đun đã cho và sau đó gọi __using__/1lại cuộc gọi trên nó cho phép mô-đun đưa một số mã vào bối cảnh hiện tại. Nói chung, các mô-đun sau:

defmodule Example do
  use Feature, option: :value
end

được biên dịch thành

defmodule Example do
  require Feature
  Feature.__using__(option: :value)
end

14

Với nền tảng từ các ngôn ngữ Python / Java / Golang, importvs usecũng bị nhầm lẫn với tôi. Điều này sẽ giải thích cơ chế tái sử dụng mã với một số ví dụ về ngôn ngữ khai báo.

nhập khẩu

Nói tóm lại, trong Elixir, bạn không cần nhập mô-đun. Tất cả các chức năng công cộng có thể được truy cập bằng cú pháp MODULE.FUNCTION đủ điều kiện:

iex()> Integer.mod(5, 2)
1

iex()> String.trim(" Hello Elixir  ")
"Hello Elixir"

Trong Python / Java / Golang, bạn cần import MODULEtrước khi có thể sử dụng các hàm trong MODULE đó, ví dụ: Python

In []: import math

In []: math.sqrt(100)
Out[]: 10.0

Sau đó, những gì importtrong Elixir có thể làm bạn ngạc nhiên:

Chúng tôi sử dụng nhập bất cứ khi nào chúng tôi muốn dễ dàng truy cập các chức năng hoặc macro từ các mô-đun khác mà không cần sử dụng tên đủ điều kiện

https://elixir-lang.org/getting-started/alias-require-and-import.html#import

Vì vậy, nếu bạn muốn gõ sqrtthay vì Integer.sqrt, trimthay vì String.trim, importsẽ giúp

iex()> import Integer
Integer
iex()> sqrt(100)
10.0

iex()> import String
String
iex()> trim(" Hello Elixir    ")
"Hello Elixir"

Điều này có thể gây ra sự cố khi đọc mã và khi có xung đột tên nên không được khuyến nghị trong Erlang (ngôn ngữ có ảnh hưởng đến Elixir). Nhưng không có quy ước như vậy trong Elixir, bạn có thể tự chịu rủi ro khi sử dụng nó.

Trong Python, hiệu ứng tương tự có thể được thực hiện bằng cách:

from math import * 

và nó chỉ được khuyến nghị sử dụng trong một số tình huống đặc biệt / chế độ tương tác - để gõ ngắn hơn / nhanh hơn.

sử dụng và yêu cầu

Điều gì làm cho use/require khác biệt là chúng liên quan đến "macro" - khái niệm không tồn tại trong gia đình Python / Java / Golang ....

Bạn không cần importmột mô-đun để sử dụng các chức năng của nó, nhưng bạn cần requiremột mô-đun để sử dụng các macro của nó :

iex()> Integer.mod(5, 3) # mod is a function
2

iex()> Integer.is_even(42)
** (CompileError) iex:3: you must require Integer before invoking the macro Integer.is_even/1
    (elixir) src/elixir_dispatch.erl:97: :elixir_dispatch.dispatch_require/6
iex()> require Integer
Integer
iex()> Integer.is_even(42) # is_even is a macro
true

Mặc dù is_evencó thể được viết như một hàm bình thường, nó là một macro vì:

Trong Elixir, Integer.is_odd / 1 được định nghĩa là macro để có thể sử dụng nó làm bảo vệ.

https://elixir-lang.org/getting-started/alias-require-and-import.html#require

use, để trích từ tài liệu Elixir:

sử dụng yêu cầu mô-đun đã cho và sau đó gọi __using__/1lại cuộc gọi trên nó cho phép mô-đun đưa một số mã vào ngữ cảnh hiện tại.

defmodule Example do
  use Feature, option: :value
end

được biên dịch thành

defmodule Example do
  require Feature
  Feature.__using__(option: :value)
end

https://elixir-lang.org/getting-started/alias-require-and-import.html#use

Vì vậy, viết use Xcũng giống như viết

require X
X.__using__()

use/2 là một macro , macro sẽ chuyển đổi mã thành mã khác cho bạn.

Bạn sẽ muốn use MODULEkhi bạn:

  • muốn truy cập các macro của nó ( require)
  • VÀ thực hiện MODULE.__using__()

Đã thử nghiệm trên Elixir 1.5


3

use Module đòi hỏi Module và cũng gọi __using__

import Modulemang Modulechức năng vào bối cảnh hiện tại , không chỉ yêu cầu nó.


0

Nhập khẩu

Làm cho tất cả các chức năng và macro từ một mô-đun nhất định có thể truy cập được trong phạm vi từ vựng nơi nó được gọi. Hãy nhớ rằng trong hầu hết các trường hợp, bạn chỉ cần nhập một hoặc nhiều hàm / macro.

Thí dụ:

defmodule TextPrinter do
  import IO, only: [puts: 1]

  def execute(text) do
    puts(text)
  end
end

iex> TextPrinter.execute("Hello")
Hello
:ok

Sử dụng

Macro này cho phép bạn tiêm bất kỳnào trong mô-đun hiện tại. Bạn nên cẩn thận khi sử dụng các thư viện bên ngoài use, vì bạn có thể không chắc chắn chính xác những gì xảy ra đằng sau hậu trường.

Thí dụ:

defmodule Printer do
  defmacro __using__(_opts) do
    quote do
      def execute(text) do
        IO.puts(text)
      end
    end
  end
end

defmodule TextPrinter do
  use Printer
end

iex> TextPrinter.execute("Hello")
Hello
:ok

Đằng sau mã cảnh bên trong __using__đã được đưa vào TextPrintermô-đun.

Nhân tiện, có nhiều hướng dẫn xử lý phụ thuộc hơn trong Elixir .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.