Reflective programming: Difference between revisions

Content deleted Content added
m cite repair;
 
(46 intermediate revisions by 14 users not shown)
Line 1:
{{Short description|Ability of a process to examine and modify itself}}
{{distinguish|Reflection (computer graphics)}}
{{Programming paradigms}}
 
In [[computer science]], '''reflective programming''' or '''reflection''' is the ability of a [[Process (computing)|process]] to examine, [[Introspection (computer science)|introspect]], and modify its own structure and behavior.<ref>{{Citation | title = A Tutorial on Behavioral Reflection and its Implementation by Jacques Malenfant et al. | publisher = unknown | url = http://www2.parc.com/csl/groups/sda/projects/reflection96/docs/malenfant/malenfant.pdf | access-date = 23 June 2019 | archive-url = https://web.archive.org/web/20170821214626/http://www2.parc.com/csl/groups/sda/projects/reflection96/docs/malenfant/malenfant.pdf | archive-date = 21 August 2017 }}</ref>
 
==Historical background==
The earliest computers were programmed in their native [[assembly language]]s, which were inherently reflective, as these original architectures could be programmed by defining instructions as data and using [[self-modifying code]]. As the bulk of programming moved to higher-level [[compiled languages]] such as [[ALGOL|Algol]], [[CobolCOBOL]], [[Fortran]], [[Pascal (programming language)|Pascal]], and [[C (programming language)|C]], this reflective ability largely disappeared until new programming languages with reflection built into their type systems appeared.{{Citation needed|date=July 2015}}
 
[[Brian Cantwell Smith]]'s 1982 doctoral dissertation introduced the notion of computational reflection in procedural [[programming languages]] and the notion of the [[meta-circular interpreter]] as a component of [[3-Lisp]].<ref>Brian Cantwell Smith, [http://hdl.handle.net/1721.1/15961 Procedural Reflection in Programming Languages], Department of Electrical Engineering and Computer Science, Massachusetts Institute of Technology, PhD dissertation, 1982.</ref><ref>Brian C. Smith. [http://publications.csail.mit.edu/lcs/specpub.php?id=840 Reflection and semantics in a procedural language] {{Webarchive|url=https://web.archive.org/web/20151213034343/http://publications.csail.mit.edu/lcs/specpub.php?id=840 |date=2015-12-13 }}. Technical Report MIT-LCS-TR-272, Massachusetts Institute of Technology, Cambridge, Massachusetts, January 1982.</ref>
 
==Uses==
Reflection helps programmers make generic software libraries to display data, process different formats of data, perform [[serialization]] orand deserialization of data for communication, or do bundling and unbundling of data for containers or bursts of communication.
 
Effective use of reflection almost always requires a plan: A design framework, encoding description, object library, a map of a database or entity relations.
Line 29 ⟶ 28:
==Implementation==
{{Unreferenced section|date=January 2008}}
A language supportingthat supports reflection provides a number of features available at runtime that would otherwise be difficult to accomplish in a lower-level language. Some of these features are the abilities to:
* Discover and modify [[Source code|source-code]] constructions (such as code blocks, [[Class (computer science)|classes]], methods, protocols, etc.) as [[first-class object]]s at [[Runtime (program lifecycle phase)|runtime]].
* Convert a [[string (computer science)|string]] matching the symbolic name of a class or function into a reference to or invocation of that class or function.
Line 47 ⟶ 46:
==Examples==
The following code snippets create an [[instance (computer science)|instance]] {{code|foo}} of [[class (computer science)|class]] {{code|Foo}} and invoke its [[method (computer science)|method]] {{code|PrintHello}}. For each [[programming language]], normal and reflection-based call sequences are shown.
 
=== Common Lisp ===
The following is an example in [[Common Lisp]] using the [[Common Lisp Object System]]:
 
<syntaxhighlight lang="lisp">
(defclass foo () ())
(defmethod print-hello ((f foo)) (format T "Hello from ~S~%" f))
 
;; Normal, without reflection
(let ((foo (make-instance 'foo)))
(print-hello foo))
 
;; With reflection to look up the class named "foo" and the method
;; named "print-hello" that specializes on "foo".
(let* ((foo-class (find-class (read-from-string "foo")))
(print-hello-method (find-method (symbol-function (read-from-string "print-hello"))
nil (list foo-class))))
(funcall (sb-mop:method-generic-function print-hello-method)
(make-instance foo-class)))
</syntaxhighlight>
 
=== C++ ===
The following is an example in [[C++]].
 
<syntaxhighlight lang="cpp">
import std;
 
using std::string_view;
using std::meta::info;
using std::views::filter;
 
class Foo {
private:
// ...
public:
void printHello() const noexcept {
std::println("Hello, world!");
}
};
 
[[nodiscard]]
consteval bool isNonstaticMethod(info mem) noexcept {
return is_class_member(mem)
&& !is_static_member(mem)
&& is_function(mem);
}
 
[[nodiscard]]
consteval info findMethod(info ty, const char* name) {
constexpr auto ctx = std::meta::access_context::current();
for (info member : members_of(ty, ctx) | filter(isNonstaticMethod)) {
if (identifier_of(member) == name) {
return member;
}
}
return info{};
}
 
template <info Ty, auto Name>
constexpr auto createInvokerImpl = []() -> auto {
using Type = [: Ty :];
static constexpr info M = findMethod(Ty, Name);
static_assert(parameters_of(M).size() == 0 && return_type_of(M) == ^^void);
return [](Type& instance) -> void { instance.[: M :](); };
}();
 
[[nodiscard]]
consteval info createInvoker(info ty, string_view name) {
return substitute(^^createInvokerImpl, {
std::meta::reflect_constant(ty),
std::meta::reflect_constant_string(name)});
}
 
int main(int argc, char* argv[]) {
Foo foo;
 
// Without reflection
foo.printHello();
 
// With reflection
auto invokePrint = [: createInvoker(^^Foo, "printHello") :];
invokePrint(foo);
 
return 0;
}
</syntaxhighlight>
 
=== C# ===
The following is an example in [[C Sharp (programming language)|C#]]:
 
<syntaxhighlight lang="c#">
using System;
// Without reflection
using System.Reflection;
var foo = new Foo();
foo.PrintHello();
 
class Foo {
// With reflection
// ...
Object foo = Activator.CreateInstance("complete.classpath.and.Foo");
public void PrintHello() {
MethodInfo method = foo.GetType().GetMethod("PrintHello");
Console.WriteLine("Hello, world!");
method.Invoke(foo, null);
}
}
 
public class InvokeFooExample {
static void Main(string[] args) {
// Without reflection
Foo foo = new Foo();
foo.PrintHello();
 
// With reflection
Object foo = Activator.CreateInstance("complete.classpath.and.Foo");
MethodInfo method = foo.GetType().GetMethod("PrintHello");
method.Invoke(foo, null);
}
}
</syntaxhighlight>
 
===Delphi /, Object Pascal===
This [[Delphi (programming languagesoftware)|Delphi]]/ and [[Object Pascal]] example assumes that a {{mono|TFoo}} class has been declared in a unit called {{mono|Unit1}}:
 
<syntaxhighlight lang="Delphi">
uses RTTI, Unit1;
Line 95 ⟶ 196:
 
===eC===
The following is an example in [[eC (programming language)|eC]]:
 
<syntaxhighlight lang=eC>
// Without reflection
Line 133 ⟶ 235:
import java.lang.reflect.Method;
 
class Foo {
// ...
public void printHello() {
System.out.println("Hello, world!");
}
}
 
public class InvokeFooExample {
public static void main(String[] args) {
// Without reflection
Foo foo = new Foo();
foo.printHello();
 
// With reflection
try {
Foo foo = Foo.class.getDeclaredConstructor().newInstance();
 
Method m = foo.getClass().getDeclaredMethod("printHello", new Class<?>[0]);
m.invoke(foo);
} catch (ReflectiveOperationException e) {
System.err.printf("An error occurred: %s%n", e.getMessage());
}
}
}
</syntaxhighlight>
 
===JavaScript/TypeScript===
The following is an example in [[JavaScript]]:
 
<syntaxhighlight lang="javascript">
// Without reflection
Fooconst foo = new Foo();
foo.hello();
 
// With reflection
const foo = Reflect.construct(Foo);
try {
const hello = Reflect.get(foo, 'hello');
Object foo = Foo.class.getDeclaredConstructor().newInstance();
Reflect.apply(hello, foo, []);
 
// With eval
Method m = foo.getClass().getDeclaredMethod("hello", new Class<?>[0]);
eval('new Foo().hello()');
m.invoke(foo);
} catch (ReflectiveOperationException ignored) {}
</syntaxhighlight>
 
The following is the same example in [[TypeScript]]:
===JavaScript===
 
The following is an example in [[JavaScript]]:
<syntaxhighlight lang="javascripttypescript">
// Without reflection
const foo: Foo = new Foo();
foo.hello();
 
// With reflection
const foo: Foo = Reflect.construct(Foo);
const hello: (this: Foo) => void = Reflect.get(foo, 'hello') as (this: Foo) => void;
Reflect.apply(hello, foo, []);
 
// With eval
eval('new Foo().hello()');
</syntaxhighlight>
 
===Julia===
The following is an example in [[Julia (programming language)|Julia]]:
<syntaxhighlight lang="julia-repl">
julia> struct Point
Line 186 ⟶ 318:
===Objective-C===
The following is an example in [[Objective-C]], implying either the [[OpenStep]] or [[Foundation Kit]] framework is used:
 
<syntaxhighlight lang="ObjC">
// Foo class.
Line 193 ⟶ 326:
 
// Sending "hello" to a Foo instance without reflection.
Foo * obj = [[Foo alloc] init];
[obj hello];
 
Line 202 ⟶ 335:
 
===Perl===
The following is an example in [[Perl (programming language)|Perl]]:
 
<syntaxhighlight lang="perl">
Line 229 ⟶ 362:
===PHP===
The following is an example in [[PHP]]:<ref>{{cite web |title=PHP: ReflectionClass - Manual |url=https://www.php.net/manual/en/class.reflectionclass.php |website=www.php.net}}</ref>
 
<syntaxhighlight lang="php">
// Without reflection
Line 243 ⟶ 377:
===Python===
The following is an example in [[Python (programming language)|Python]]:
 
<syntaxhighlight lang="python">
from typing import Any
# Without reflection
obj = Foo()
obj.hello()
 
class Foo:
# With reflection
# ...
obj = globals()["Foo"]()
def print_hello() -> None:
getattr(obj, "hello")()
print("Hello, world!")
 
if __name__ == "__main__":
# With eval
# Without reflection
eval("Foo().hello()")
obj: Foo = Foo()
obj.print_hello()
 
# With reflection
obj: Foo = globals()["Foo"]()
_: Any = getattr(obj, "print_hello")()
 
# With eval
eval("Foo().print_hello()")
</syntaxhighlight>
 
===R===
The following is an example in [[R (programming language)|R]]:
 
<syntaxhighlight lang="r">
# Without reflection, assuming foo() returns an S3-type object that has method "hello"
Line 271 ⟶ 415:
 
===Ruby===
The following is an example in [[Ruby (Programmingprogramming Languagelanguage)|Ruby]]:
 
<syntaxhighlight lang="ruby">
# Without reflection
Line 283 ⟶ 428:
# With eval
eval "Foo.new.hello"
</syntaxhighlight>
 
===Rust===
[[Rust (programming language)|Rust]] does not have compile-time reflection in the standard library, but it is possible using some third-party libraries such as "[https://docs.rs/bevy_reflect/latest/bevy_reflect/ {{mono|bevy_reflect}}]".
 
<syntaxhighlight lang="rust">
use std::any::TypeId;
 
use bevy_reflect::prelude::*;
use bevy_reflect::{
FunctionRegistry,
GetTypeRegistration,
Reflect,
ReflectFunction,
ReflectFunctionRegistry,
ReflectMut,
ReflectRef,
TypeRegistry
};
 
#[derive(Reflect)]
#[reflect(DoFoo)]
struct Foo {
// ...
}
 
impl Foo {
fn new() -> Self {
Foo {}
}
 
fn print_hello(&self) {
println!("Hello, world!");
}
}
 
#[reflect_trait]
trait DoFoo {
fn print_hello(&self);
}
 
impl DoFoo for Foo {
fn print_hello(&self) {
self.print_hello();
}
}
 
fn main() {
// Without reflection
let foo: Foo = Foo::new();
foo.print_hello();
 
// With reflection
let mut registry: TypeRegistry = TypeRegistry::default();
 
registry.register::<Foo>();
registry.register_type_data::<Foo, ReflectFunctionRegistry>();
registry.register_type_data::<Foo, ReflectDoFoo>();
 
let foo: Foo = Foo;
let reflect_foo: Box<dyn Reflect> = Box::new(foo);
 
// Version 1: call hello by trait
let trait_registration: &ReflectDoFoo = registry
.get_type_data::<ReflectDoFoo>(TypeId::of::<Foo>())
.expect("ReflectDoFoo not found for Foo");
 
let trait_object: &dyn DoFoo = trait_registration
.get(&*reflect_foo)
.expect("Failed to get DoFoo trait object");
 
trait_object.print_hello();
 
// Version 2: call hello by function name
let func_registry: &FunctionRegistry = registry
.get_type_data::<FunctionRegistry>(TypeId::of::<Foo>())
.expect("FunctionRegistry not found for Foo");
 
if let Some(dyn_func) = func_registry.get("print_hello") {
let result: Option<Box<dyn Reflect>> = dyn_func
.call(&*reflect_foo, Vec::<Box<dyn Reflect>>::new())
.ok();
 
if result.is_none() {
println!("Function called, no result returned (as expected for void return)");
}
} else {
println!("No function named hello found in FunctionRegistry");
}
}
</syntaxhighlight>
 
===Xojo===
The following is an example using [[Xojo]]:
 
<syntaxhighlight lang="vbnet">
' Without reflection
Line 332 ⟶ 568:
* [http://www.laputan.org/#Reflection Brian Foote's pages on Reflection in Smalltalk]
* [http://docs.oracle.com/javase/tutorial/reflect/index.html Java Reflection API Tutorial] from Oracle
{{Programming paradigms navbox}}
{{Types of programming languages}}
 
{{DEFAULTSORT:Reflection (Computer Programming)}}
[[Category:Programming constructs]]
[[Category:Programming language comparisons]]
<!-- Hidden categories below -->
[[Category:Articles with example BASIC code]]
[[Category:Articles with example C code]]
[[Category:Articles with example C Sharp code]]
[[Category:Articles with example Java code]]
[[Category:Articles with example JavaScript code]]
[[Category:Articles with example Julia code]]
[[Category:Articles with example Lisp (programming language) code]]
[[Category:Articles with example Objective-C code]]
[[Category:Articles with example Pascal code]]
[[Category:Articles with example Perl code]]
[[Category:Articles with example PHP code]]
[[Category:Articles with example Python (programming language) code]]
[[Category:Articles with example R code]]
[[Category:Articles with example Ruby code]]