Elixir: ใช้เทียบกับการนำเข้า


139

อะไรคือความแตกต่างระหว่างuseและimport?

การใช้งานเป็นกลไกง่ายๆในการใช้โมดูลที่กำหนดในบริบทปัจจุบัน

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

นำเข้าฟังก์ชันและมาโครจากโมดูลอื่น ๆ

ดูเหมือนความแตกต่างอย่างหนึ่งคือimportให้คุณเลือกฟังก์ชัน / มาโครเฉพาะในขณะที่useนำทุกอย่างเข้ามา

มีความแตกต่างอื่น ๆ หรือไม่? คุณจะใช้เมื่อใด


สรุปย่อ: import Moduleนำเสนอฟังก์ชั่นที่จะใช้ภายในโมดูลของคุณ use Moduleนำฟังก์ชั่นที่จะใช้และเปิดเผยต่อสาธารณะในโมดูลของคุณ
Jered

คำตอบ:


216

import Moduleนำฟังก์ชันและมาโครทั้งหมดของModuleun-namespaced มาไว้ในโมดูลของคุณ

require Moduleอนุญาตให้คุณใช้มาโครModuleแต่ไม่นำเข้า (ฟังก์ชันของModuleเนมสเปซจะพร้อมใช้งานเสมอ)

use Moduleแรกrequiresโมดูลแล้วเรียกแมโคร__using__Module

พิจารณาสิ่งต่อไปนี้:

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

นี้จะไม่รวบรวมเป็นยังไม่ได้ถูกนำเข้ามายังModA.moda()ModB

ต่อไปนี้จะรวบรวมแม้ว่า:

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

เมื่อคุณuseผ่านModAมันสร้างข้อคำสั่งที่ถูกใส่เข้าไปในimportModB


6
ตอบโจทย์มาก! สำหรับข้อมูลเพิ่มเติม: elixir-lang.org/getting-started/alias-require-and-import.html
justin

การเข้าสู่ Elixir และการมาจากโลก Python ฉันค่อนข้างสับสนเกี่ยวกับโมดูลที่เป็น*.exไฟล์และdefmoduleบล็อกและวิธีที่คุณดึงโมดูลจากไฟล์ไปยัง IEX REPL
Nick T

2
พยายามทำความเข้าใจตัวอย่าง / แนวคิด ในกรณีนี้คุณเพียงแค่แสดงให้เห็นว่ามี__using__การดำเนินการวิธีการuse ModA? มันอาจจะสมเหตุสมผลที่จะใช้การนำเข้าในModBตัวอย่างที่คุณนำเสนอถูกต้องหรือไม่?
Ryan-Neal Mes

35

useมีไว้สำหรับการฉีดโค้ดลงในโมดูลปัจจุบันในขณะที่importใช้เพื่อนำเข้าฟังก์ชันเพื่อใช้งาน คุณสามารถสร้างการuseใช้งานที่นำเข้าฟังก์ชั่นโดยอัตโนมัติเช่นฉันทำกับ Timex เมื่อคุณเพิ่มลงuse Timexในโมดูลลองดูที่ timex.ex หากคุณต้องการทราบว่าฉันหมายถึงอะไรนี่เป็นตัวอย่างง่ายๆในการสร้าง โมดูลซึ่งสามารถเป็นuse'd


1
มันถูกต้องหรือไม่ที่จะพูดว่าuseเป็นเรื่องทั่วไปมากกว่าimport? นั่นคือการทำงานของimportเป็นส่วนย่อยของuse
User314159

1
importยังจำเป็นอยู่เพราะฉันไม่รู้ว่ามันถูกต้องหรือไม่ที่จะบอกว่าคุณสามารถนำไปใช้ใหม่ได้importด้วยตัวuseคนเดียว แต่ฉันจะไม่แปลกใจเลยถ้าเป็นไปได้ useมีพลังมากกว่าอย่างแน่นอน คุณสามารถทำสิ่งที่ซับซ้อนได้ด้วยตัวอย่างเช่นฉันใช้ประโยชน์อย่างหนักuseในexprotobufโครงการของฉันซึ่งคุณสามารถตรวจสอบได้ว่าคุณต้องการเห็นว่ามันถูกผลักไปถึงขีด จำกัด คุณสามารถขยายโมดูลด้วยโค้ดรันโค้ดในเวลาคอมไพล์เพิ่มฟังก์ชันให้กับโมดูล ฯลฯ โดยทั่วไปจะรวมimportและพลังของมาโคร
bitwalker

ขอบคุณสำหรับคำอธิบายโดยละเอียดและการอ้างอิงถึงรหัส ฉันคิดว่าฉันเข้าใจแล้ว ฉันยังใหม่กับ Elixir แต่ฉันคิดว่าเมื่อฉันดูกรณีการใช้งานมากขึ้นความแตกต่างจะชัดเจน
User314159

เฮ้ไม่มีปัญหาสถานที่ที่น่าสนใจอีกแห่งหนึ่งคือ Phoenix web framework Chris McCord เขียนหนังสือเกี่ยวกับมาโคร Elixir และเขาใช้มันอย่างมากใน Phoenix (รวมถึงuse) การอ่านสำหรับผู้เริ่มต้นจะง่ายกว่าแน่นอนexprotobufแต่ฉันคิดว่าฉันอาจผลักดันuseให้ถึงขีด จำกัดexprotobufดังนั้นจึงอาจมีประโยชน์เพียงเพื่อดูว่าคุณจะไปได้ไกลแค่ไหน
bitwalker

5
useจริงๆแล้วไม่ได้ทำอะไรมากเพียงแค่เรียก__using__ใช้โมดูลที่ระบุ
Patrick Oscity

25

ดูหน้า«นามแฝงต้องการและนำเข้า»จากคู่มือการเริ่มต้นอย่างเป็นทางการของ Elixir:

# 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

จำเป็นต้อง

Elixir จัดเตรียมมาโครเป็นกลไกสำหรับการเขียนโปรแกรมเมตา (การเขียนโค้ดที่สร้างโค้ด)

มาโครเป็นส่วนของโค้ดที่เรียกใช้และขยายในเวลาคอมไพล์ ซึ่งหมายความว่าในการใช้มาโครเราจำเป็นต้องรับประกันว่าโมดูลและการใช้งานจะพร้อมใช้งานในระหว่างการคอมไพล์ สิ่งนี้ทำได้ด้วยrequireคำสั่ง

โดยทั่วไปไม่จำเป็นต้องใช้โมดูลก่อนการใช้งานยกเว้นว่าเราต้องการใช้มาโครที่มีอยู่ในโมดูลนั้น

นำเข้า

เราใช้importเมื่อใดก็ตามที่เราต้องการเข้าถึงฟังก์ชันหรือมาโครจากโมดูลอื่น ๆ ได้อย่างง่ายดายโดยไม่ต้องใช้ชื่อที่มีคุณสมบัติครบถ้วน ตัวอย่างเช่นหากเราต้องการใช้duplicate/2ฟังก์ชันจากListโมดูลหลาย ๆ ครั้งเราสามารถนำเข้าได้:

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

ในกรณีนี้เรากำลังนำเข้าเพียงฟังก์ชั่นduplicate(กับ arity 2) Listจาก

โปรดทราบว่าimportไอเอ็นจีโมดูลโดยอัตโนมัติrequireล่ะ

ใช้

แม้ว่าจะไม่ใช่คำสั่ง แต่useเป็นมาโครที่เกี่ยวข้องอย่างแน่นหนาrequireซึ่งช่วยให้คุณใช้โมดูลในบริบทปัจจุบันได้ useแมโครมักจะถูกใช้โดยนักพัฒนาที่จะนำการทำงานภายนอกเข้าไปในขอบเขตของคำศัพท์ในปัจจุบันมักจะโมดูล

เบื้องหลังuseต้องใช้โมดูลที่กำหนดจากนั้นเรียกการ__using__/1เรียกกลับจากนั้นให้โมดูลฉีดโค้ดบางส่วนลงในบริบทปัจจุบัน โดยทั่วไปโมดูลต่อไปนี้:

defmodule Example do
  use Feature, option: :value
end

รวบรวมเป็นไฟล์

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

14

ด้วยพื้นหลังจากภาษา Python / Java / Golang importเทียบกับuseฉันก็สับสนเช่นกัน สิ่งนี้จะอธิบายกลไกการใช้โค้ดซ้ำพร้อมตัวอย่างภาษาที่เปิดเผย

นำเข้า

กล่าวโดยย่อใน Elixir คุณไม่จำเป็นต้องนำเข้าโมดูล ฟังก์ชันสาธารณะทั้งหมดสามารถเข้าถึงได้โดยไวยากรณ์ MODULE ที่มีคุณสมบัติครบถ้วนฟังก์ชัน FUNCTION:

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

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

ใน Python / Java / Golang คุณต้องทำimport MODULEก่อนจึงจะสามารถใช้ฟังก์ชันใน MODULE นั้นได้เช่น Python

In []: import math

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

แล้วสิ่งที่importอาจทำให้คุณประหลาดใจใน Elixir:

เราใช้การนำเข้าเมื่อใดก็ตามที่เราต้องการเข้าถึงฟังก์ชันหรือมาโครจากโมดูลอื่น ๆ ได้อย่างง่ายดายโดยไม่ต้องใช้ชื่อที่มีคุณสมบัติครบถ้วน

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

ดังนั้นหากคุณต้องการที่จะพิมพ์sqrtแทนInteger.sqrt, trimแทนString.trim, importจะช่วยให้

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

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

สิ่งนี้อาจทำให้เกิดปัญหาในการอ่านโค้ดและเมื่อชื่อขัดแย้งกันจึงไม่แนะนำให้ใช้ใน Erlang (ภาษาที่มีผลต่อ Elixir) แต่ไม่มีข้อตกลงดังกล่าวใน Elixir คุณสามารถใช้มันโดยเสี่ยงเอง

ใน Python เอฟเฟกต์เดียวกันสามารถทำได้โดย:

from math import * 

และขอแนะนำให้ใช้เฉพาะในสถานการณ์พิเศษ / โหมดโต้ตอบ - สำหรับการพิมพ์ที่สั้นลง / เร็วขึ้น

ใช้และต้องการ

สิ่งที่ทำให้use/ requireแตกต่างคือมันเกี่ยวข้องกับ "มาโคร" - แนวคิดที่ไม่มีอยู่ในตระกูล Python / Java / Golang ...

คุณไม่จำเป็นต้องimportใช้โมดูลเพื่อใช้ฟังก์ชันต่างๆ แต่คุณต้องrequireใช้โมดูลเพื่อใช้มาโคร :

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

แม้ว่าจะis_evenสามารถเขียนเป็นฟังก์ชันปกติได้ แต่ก็เป็นมาโครเนื่องจาก:

ใน Elixir Integer.is_odd / 1 ถูกกำหนดเป็นมาโครเพื่อให้สามารถใช้เป็นตัวป้องกันได้

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

useเพื่อตัดตอนมาจากเอกสาร Elixir:

การใช้งานต้องใช้โมดูลที่กำหนดจากนั้นเรียกการ__using__/1เรียกกลับโดยให้โมดูลฉีดโค้ดบางส่วนลงในบริบทปัจจุบัน

defmodule Example do
  use Feature, option: :value
end

รวบรวมเป็นไฟล์

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

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

ดังนั้นการเขียนuse Xก็เหมือนกับการเขียน

require X
X.__using__()

use/2 เป็นมาโครมาโครจะแปลงรหัสเป็นรหัสอื่นให้คุณ

คุณจะต้องการuse MODULEเมื่อคุณ:

  • ต้องการเข้าถึงมาโคร ( require)
  • และดำเนินการ MODULE.__using__()

ทดสอบกับ Elixir 1.5


3

use Module ต้องการ Moduleและเรียก__using__มันด้วย

import ModuleนำModuleฟังก์ชันการทำงานมาใช้ในบริบทปัจจุบันไม่ใช่แค่ต้องการเท่านั้น


0

นำเข้า

ทำให้ฟังก์ชันและมาโครทั้งหมดจากโมดูลที่กำหนดสามารถเข้าถึงได้ภายในขอบเขตคำศัพท์ที่เรียก โปรดทราบว่าในกรณีส่วนใหญ่คุณต้องนำเข้าฟังก์ชัน / มาโครเพียงหนึ่งฟังก์ชันขึ้นไป

ตัวอย่าง:

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

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

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

ใช้

มาโครนี้ช่วยให้คุณฉีดโค้ดใด ๆในโมดูลปัจจุบัน คุณควรระมัดระวังเมื่อใช้ไลบรารีภายนอกด้วยuseเนื่องจากคุณอาจไม่แน่ใจว่าเกิดอะไรขึ้นเบื้องหลัง

ตัวอย่าง:

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

ด้านหลังโค้ดฉากที่อยู่ด้านใน__using__ถูกใส่เข้าไปในTextPrinterโมดูล

โดยวิธีการที่มีการจัดการคำแนะนำในการพึ่งพายาอายุวัฒนะมากขึ้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.