Erlang (lenguaje de programación)

Compartir Imprimir Citar

Erlang (UR-lang) es un lenguaje de programación funcional, concurrente y de propósito general, y un sistema de tiempo de ejecución de recolección de elementos no utilizados. El término Erlang se usa indistintamente con Erlang/OTP, o Open Telecom Platform (OTP), que consta del sistema de tiempo de ejecución de Erlang, varios componentes listos para usar (OTP) escritos principalmente en Erlang y un conjunto de principios de diseño para Erlang. programas

El sistema de tiempo de ejecución de Erlang está diseñado para sistemas con estas características:

El lenguaje de programación Erlang tiene datos inmutables, coincidencia de patrones y programación funcional. El subconjunto secuencial del lenguaje Erlang admite la evaluación entusiasta, la asignación única y la escritura dinámica.

Una aplicación normal de Erlang se construye a partir de cientos de pequeños procesos de Erlang.

Originalmente era un software propietario dentro de Ericsson, desarrollado por Joe Armstrong, Robert Virding y Mike Williams en 1986, pero se lanzó como software gratuito y de código abierto en 1998. Erlang/OTP cuenta con el respaldo y el mantenimiento de Open Telecom Platform. (OTP) unidad de producto en Ericsson.

Historia

El nombre Erlang, atribuido a Bjarne Däcker, ha sido supuesto por quienes trabajan en los conmutadores de telefonía (para quienes se diseñó el lenguaje) como una referencia al matemático e ingeniero danés Agner Krarup Erlang y una abreviatura silábica de "Ericsson Language". Erlang fue diseñado con el objetivo de mejorar el desarrollo de aplicaciones de telefonía. La versión inicial de Erlang se implementó en Prolog y estuvo influenciada por el lenguaje de programación PLEX utilizado en intercambios anteriores de Ericsson. En 1988, Erlang había demostrado que era adecuado para crear prototipos de centrales telefónicas, pero el intérprete de Prolog era demasiado lento. Un grupo dentro de Ericsson estimó que tendría que ser 40 veces más rápido para ser adecuado para su uso en producción. En 1992, se comenzó a trabajar en la máquina virtual (VM) BEAM que compila Erlang a C utilizando una combinación de código compilado de forma nativa y código enhebrado para lograr un equilibrio entre el rendimiento y el espacio en disco. Según el co-inventor Joe Armstrong, el lenguaje pasó de ser un producto de laboratorio a aplicaciones reales luego del colapso de la central telefónica AX de próxima generación llamada AXE-N en 1995. Como resultado, Erlang fue elegido para el próximo modo de transferencia asíncrono (ATM).) intercambiar AXD.

Robert Virding y Joe Armstrong, 2013
Mike Williams

En febrero de 1998, Ericsson Radio Systems prohibió el uso interno de Erlang para nuevos productos, citando una preferencia por lenguajes no patentados. La prohibición hizo que Armstrong y otros hicieran planes para dejar Ericsson. En marzo de 1998, Ericsson anunció el conmutador AXD301, que contenía más de un millón de líneas de Erlang y logró una alta disponibilidad de nueve '9'. En diciembre de 1998, la implementación de Erlang fue de código abierto y la mayor parte del equipo de Erlang renunció para formar una nueva empresa, Bluetail AB. Ericsson finalmente relajó la prohibición y volvió a contratar a Armstrong en 2004.

En 2006, se agregó compatibilidad nativa con multiprocesamiento simétrico al sistema de tiempo de ejecución y la máquina virtual.

Procesos

Las aplicaciones de Erlang se crean a partir de procesos de Erlang muy ligeros en el sistema de tiempo de ejecución de Erlang. Los procesos de Erlang se pueden ver como "vivos" objetos (programación orientada a objetos), con encapsulación de datos y paso de mensajes, pero capaz de cambiar el comportamiento durante el tiempo de ejecución. El sistema de tiempo de ejecución de Erlang proporciona un aislamiento de proceso estricto entre los procesos de Erlang (esto incluye datos y recolección de elementos no utilizados, separados individualmente por cada proceso de Erlang) y comunicación transparente entre procesos (consulte Transparencia de ubicación) en diferentes nodos de Erlang (en diferentes hosts).

Joe Armstrong, co-inventor de Erlang, resumió los principios de los procesos en su tesis doctoral:

Joe Armstrong comentó en una entrevista con Rackspace en 2013: "Si Java es 'escribe una vez, ejecuta en cualquier lugar', entonces Erlang es 'escribe una vez, ejecuta para siempre'. ”

Uso

En 2014, Ericsson informó que Erlang estaba siendo utilizado en sus nodos de soporte y en redes móviles GPRS, 3G y LTE en todo el mundo y también por Nortel y T-Mobile.

Erlang se usa en RabbitMQ. Como expresó Tim Bray, director de Tecnologías Web de Sun Microsystems, en su discurso de apertura en la Convención de código abierto de O'Reilly (OSCON) en julio de 2008:

Si alguien vino a mí y quería pagarme mucho dinero para construir un sistema de manejo de mensajes de gran escala que realmente tenía que estar levantado todo el tiempo, nunca podría permitirme bajar por años a la vez, yo elegiría sin duda Erlang para construirlo.

Erlang es el lenguaje de programación utilizado para codificar WhatsApp.

Elixir es un lenguaje de programación que se compila en código de bytes BEAM (a través del formato abstracto de Erlang).

Desde su lanzamiento como código abierto, Erlang se ha extendido más allá de las telecomunicaciones, estableciéndose en otros mercados verticales como FinTech, juegos, atención médica, automotriz, Internet de las cosas y blockchain. Aparte de WhatsApp, hay otras empresas que figuran como casos de éxito de Erlang: Vocalink (una empresa de MasterCard), Goldman Sachs, Nintendo, AdRoll, Grindr, BT Mobile, Samsung, OpenX y SITA.

Ejemplos de programación funcional

Factoriales

Un algoritmo factorial implementado en Erlang:

-módulo()hecho). % Este es el archivo 'fact.erl', el módulo y el nombre de archivo deben coincidir-Exportación[fac/1]). % Esto exporta la función 'fac' de la aridad 1 (1 parámetro, sin tipo, sin nombre)fac()0) - 1; % Si 0, entonces devuelva 1, de lo contrario (nota el semicolon; que significa 'else')fac()N) cuando N  0, is_integer()N) - N * fac()N-1).% Determinar Recursivamente, luego devolver el resultado% (nota el período. significa 'endif' o 'función final')%% Esta función se estrellará si se da algo más que un entero no negativo.%% Ilustra la filosofía "Que se estrelle" de Erlang.

Secuencia de Fibonacci

Un algoritmo recursivo de cola que produce la secuencia de Fibonacci:

%% La declaración del módulo debe coincidir con el nombre del archivo "series.erl" -módulo()serie).%% La declaración de exportación contiene una lista de todas las funciones que forman%% de la API pública del módulo. En este caso, este módulo expone un solo%% función llamada fib que toma 1 argumento (I.E. tiene una aridad de 1)%% La sintaxis general para -export es una lista que contiene el nombre y%% aridad de cada función pública-Exportación[fib/1]).%% ---Porcentaje de API pública%% ---%% Casos de mango en los que fib/1 recibe valores específicos%% El orden en que se declaran estas firmas de funciones es vital%% parte de la funcionalidad de este móduloSi el fib/1 se pasa precisamente el entero 0, entonces regresa 0fib()0) - 0;%% Si fib/1 recibe un número negativo, entonces devuelva el átomo err_neg_valNormalmente, tal codificación defensiva se desalienta debido a que Erlang 'Let%% de la filosofía de Crash; sin embargo, en este caso deberíamos explícitamenteEvitar una situación que chocará el motor de Erlangfib()N) cuando N . 0 - err_neg_val;Si el fib/1 se pasa un entero menos de 3, entonces regresa 1%% Las dos firmas de funciones anteriores manejan todos los casos en los que N%% por lo que esta firma de función maneja casos donde N = 1 o N = 2fib()N) cuando N . 3 - 1;%% Para todos los demás valores, llame a la función privada fib_int/3 para realizar%% del cálculofib()N) - fib_int()N, 0, 1).%% ---Porcentaje de API privada%% ---%% Si fib_int/3 recibe un 1 como su primer argumento, entonces hemos terminado, así queDevuelve el valor en el argumento B. Ya que no estamos interesados en%% de valor del segundo argumento, denotamos esto usando _ para indicar un################################################################################################################################################################################################################################################################fib_int()1, _ B) - B;%% Para todas las otras combinaciones de argumentos, vuelva a llamar fib_int/3%% donde cada llamada hace lo siguiente:Decremento contado NTome el valor fibonacci anterior en el argumento B y pasarlo como% argumentativo A%% - Calcular el valor del número actual de fibonacci y pasarlo%% como argumento Bfib_int()N, A, B) - fib_int()N-1, B, A+B).

Aquí está el mismo programa sin los comentarios explicativos:

-módulo()serie).-Exportación[fib/1]).fib()0) - 0;fib()N) cuando N . 0 - err_neg_val;fib()N) cuando N . 3 - 1;fib()N) - fib_int()N, 0, 1).fib_int()1, _ B) - B;fib_int()N, A, B) - fib_int()N-1, B, A+B).

Ordenación rápida

Quicksort en Erlang, utilizando la comprensión de listas:

% qsort:qsort(List)%% Ordenar una lista de artículos-módulo()qsort). % Este es el archivo 'qsort.erl '-Exportación[qsort/1]). % Una función 'qsort' con 1 parámetro se exporta (no tipo, no nombre)qsort([]) - []; % Si la lista [] está vacía, devuelve una lista vacía (nada para ordenar)qsort[PivotSilencioDescanso]) - % Compose recursivamente una lista con 'Front' para todos los elementos que deben ser antes de 'Pivot' % entonces 'Pivot' y 'Back' para todos los elementos que deben ser después de 'Pivot' ' qsort[Frente Silencio Frente . Descanso, Frente . Pivot]) ++  [Pivot] ++ qsort[Atrás Silencio Atrás . Descanso, Atrás >= Pivot]).

El ejemplo anterior invoca recursivamente la función qsort hasta que no queda nada por ordenar. La expresión [Frente || Frente <- Resto, Frente < Pivot] es una lista por comprensión, lo que significa "Construir una lista de elementos Front de modo que Front sea miembro de Rest, y Front es menor que Pivot." ++ es el operador de concatenación de listas.

Se puede usar una función de comparación para estructuras más complicadas en aras de la legibilidad.

El siguiente código ordenaría las listas según su longitud:

% Este es el archivo 'listsort.erl' (el compilador se hace de esta manera)-módulo()listsort).% Exportar 'by_length' con 1 parámetro (no importa el tipo y el nombre)-Exportación[by_length/1]).by_length()Listas) - % Use 'qsort/2' y proporciona una función anónima como parámetro qsort()Listas, diversión()A,B) - longitud()A) . longitud()B) final).qsort([], _)- []; % Si la lista está vacía, devuelve una lista vacía (ignore el segundo parámetro)qsort[PivotSilencioDescanso] Más pequeña) - % Lista de partición con elementos 'Smaller' delante de elementos 'Pivot' y no-'Smaller' % después de 'Pivot' y ordenar las sublistas. qsort[X Silencio X . Descanso, Más pequeña()X,Pivot), Más pequeña) ++ [Pivot] ++ qsort[Y Silencio Y . Descanso, no()Más pequeña()Y, Pivot)], Más pequeña).

Se toma un Pivot del primer parámetro dado a qsort() y el resto de Lists se llama Rest. Nótese que la expresión

[X Silencio X . Descanso, Más pequeña()X,Pivot)

no es diferente en forma de

[Frente Silencio Frente . Descanso, Frente . Pivot]

(en el ejemplo anterior) excepto por el uso de una función de comparación en la última parte, diciendo "Construya una lista de elementos X tal que X es miembro de Rest, y Smaller es true", con Smaller definido anteriormente como

diversión()A,B) - longitud()A) . longitud()B) final

La función anónima se denomina Smaller en la lista de parámetros de la segunda definición de qsort para que pueda ser referenciada por ese nombre dentro de esa función. No se menciona en la primera definición de qsort, que se ocupa del caso base de una lista vacía y, por lo tanto, no necesita esta función, y mucho menos un nombre para ella.

Tipos de datos

Erlang tiene ocho tipos de datos primitivos:

Integers
Los enteros se escriben como secuencias de dígitos decimales, por ejemplo, 12, 12375 y -23427 son enteros. El aritmético entero es exacto y sólo limitado por la memoria disponible en la máquina. (Esto se llama aritmética arbitraria-precisión).
Atoms
Los átomos se utilizan dentro de un programa para denotar valores distinguidos. Están escritas como cuerdas de caracteres alfanuméricos consecutivos, siendo el primer personaje inferior. Los átomos pueden contener cualquier personaje si están encerrados dentro de citas individuales y existe una convención de escape que permite que cualquier personaje sea utilizado dentro de un átomo. Los átomos nunca se recogen basura y deben usarse con precaución, especialmente si usan la generación dinámica de átomos.
Flotas
Los números de puntos flotantes utilizan la representación de 64 bits IEEE 754.
Referencias
Las referencias son símbolos únicos a nivel mundial cuya única propiedad es que pueden compararse por igualdad. Se crean evaluando el primitivo Erlang make_ref().
Binarios
Un binario es una secuencia de bytes. Los binarios proporcionan una manera eficiente del espacio de almacenar datos binarios. Los primitivos Erlang existen para componer y descomponer los binarios y para la entrada y salida eficientes de los binarios.
Pids
Pid es corto para proceso identificador– un Pid es creado por el primitivo Erlang spawn(...) Los pids son referencias a los procesos de Erlang.
Puertos
Los puertos se utilizan para comunicarse con el mundo exterior. Los puertos se crean con la función incorporada open_port. Los mensajes pueden ser enviados y recibidos de puertos, pero estos mensajes deben obedecer al llamado "protocolo de puerto".
Funs
Las funciones son los cierres de funciones. Funciones son creadas por expresiones de la forma: fun(...) ->... end.

Y tres tipos de datos compuestos:

Tuples
Los tuplas son contenedores para un número fijo de tipos de datos de Erlang. La sintaxis {D1,D2,...,Dn} denota un tuple cuyos argumentos son D1, D2,... Dn. Los argumentos pueden ser tipos de datos primitivos o tipos de datos compuestos. Cualquier elemento de un tuple se puede acceder en tiempo constante.
Listas
Las listas son contenedores para un número variable de tipos de datos de Erlang. La sintaxis [Dh|Dt] denota una lista cuyo primer elemento es Dh, y cuyos elementos restantes son la lista Dt. La sintaxis [] denota una lista vacía. La sintaxis [D1,D2,..,Dn] es corto para [D1|[D2|..|[Dn|[]]]]. El primer elemento de una lista se puede acceder en tiempo constante. El primer elemento de una lista se llama el cabeza de la lista. El resto de una lista cuando se ha quitado la cabeza se llama el cola de la lista.
Mapas
Los mapas contienen un número variable de asociaciones de valor clave. La sintaxis es#{Key1=>Value1,...,KeyN=>ValueN}.

Se proporcionan dos formas de azúcar sintáctico:

Pendientes
Las cuerdas están escritas como listas doblemente citadas de caracteres. Esto es azúcar sintáctica para una lista de los puntos de código Unicode entero para los personajes de la cadena. Así, por ejemplo, la cuerda "cat" es corta para [99,97,116].
Documentos
Los registros proporcionan una manera conveniente para asociar una etiqueta con cada uno de los elementos en un tuple. Esto permite referirse a un elemento de un tuple por nombre y no por posición. Un precompilador toma la definición de registro y la reemplaza con la referencia de tuple apropiada.

Erlang no tiene ningún método para definir clases, aunque hay bibliotecas externas disponibles.

"Deja que se estrelle" estilo de codificación

Erlang está diseñado con un mecanismo que facilita que los procesos externos supervisen bloqueos (o fallas de hardware), en lugar de un mecanismo en proceso como el manejo de excepciones que se usa en muchos otros lenguajes de programación. Los bloqueos se informan como otros mensajes, que es la única forma en que los procesos pueden comunicarse entre sí, y los subprocesos pueden generarse de manera económica (ver más abajo). El "déjalo estrellarse" la filosofía prefiere que un proceso se reinicie por completo en lugar de tratar de recuperarse de una falla grave. Aunque todavía requiere el manejo de errores, esta filosofía da como resultado menos código dedicado a la programación defensiva donde el código de manejo de errores es altamente contextual y específico.

Árboles supervisores

Una aplicación típica de Erlang se escribe en forma de árbol supervisor. Esta arquitectura se basa en una jerarquía de procesos en la que el proceso de nivel superior se conoce como "supervisor". Luego, el supervisor genera múltiples procesos secundarios que actúan como trabajadores o más supervisores de nivel inferior. Tales jerarquías pueden existir en profundidades arbitrarias y se ha demostrado que proporcionan un entorno altamente escalable y tolerante a fallas dentro del cual se puede implementar la funcionalidad de la aplicación.

Dentro de un árbol de supervisores, todos los procesos de supervisores son responsables de administrar el ciclo de vida de sus procesos secundarios, y esto incluye el manejo de situaciones en las que esos procesos secundarios fallan. Cualquier proceso puede convertirse en supervisor generando primero un proceso secundario y luego llamando a erlang:monitor/2 en ese proceso. Si el proceso monitoreado falla, el supervisor recibirá un mensaje que contiene una tupla cuyo primer miembro es el átomo 'DOWN'. El supervisor es responsable, en primer lugar, de escuchar dichos mensajes y, en segundo lugar, de tomar las medidas adecuadas para corregir la condición de error.

Orientación a la concurrencia y distribución

La principal fortaleza de Erlang es la compatibilidad con la concurrencia. Tiene un pequeño pero poderoso conjunto de primitivas para crear procesos y comunicarse entre ellos. Erlang es conceptualmente similar al lenguaje occam, aunque reformula las ideas de comunicación de procesos secuenciales (CSP) en un marco funcional y utiliza el paso de mensajes asíncronos. Los procesos son el medio principal para estructurar una aplicación Erlang. No son procesos ni subprocesos del sistema operativo, sino procesos ligeros programados por BEAM. Al igual que los procesos del sistema operativo (pero a diferencia de los subprocesos del sistema operativo), no comparten estado entre sí. La sobrecarga mínima estimada para cada uno es de 300 palabras. Por lo tanto, se pueden crear muchos procesos sin degradar el rendimiento. En 2005, se realizó con éxito un benchmark con 20 millones de procesos con Erlang de 64 bits en una máquina con 16 GB de memoria de acceso aleatorio (RAM; total de 800 bytes/proceso). Erlang admite el multiprocesamiento simétrico desde el lanzamiento R11B de mayo de 2006.

Si bien los subprocesos necesitan soporte de biblioteca externa en la mayoría de los idiomas, Erlang proporciona funciones a nivel de idioma para crear y administrar procesos con el objetivo de simplificar la programación simultánea. Aunque toda la simultaneidad es explícita en Erlang, los procesos se comunican mediante el paso de mensajes en lugar de variables compartidas, lo que elimina la necesidad de bloqueos explícitos (la VM aún usa internamente un esquema de bloqueo).

La comunicación entre procesos funciona a través de un sistema de paso de mensajes asincrónico sin nada compartido: cada proceso tiene un "buzón de correo", una cola de mensajes que han sido enviados por otros procesos y aún no consumidos. Un proceso usa la primitiva receive para recuperar mensajes que coinciden con los patrones deseados. Una rutina de manejo de mensajes prueba los mensajes a su vez contra cada patrón, hasta que uno de ellos coincide. Cuando el mensaje se consume y se elimina del buzón, el proceso reanuda la ejecución. Un mensaje puede comprender cualquier estructura de Erlang, incluidas las primitivas (enteros, flotantes, caracteres, átomos), tuplas, listas y funciones.

El siguiente ejemplo de código muestra el soporte integrado para procesos distribuidos:

 % Crear un proceso e invocar la web función:start_server(Port, MaxConnections) ServerProcess = castaño()web, start_server, [Puerto, MaxConnections]), % Crear un proceso remoto e invocar la función % web:start_server(Port, MaxConnections) en la máquina RemoteNode RemoteProcess = castaño()RemoteNode, web, start_server, [Puerto, MaxConnections]), % Enviar un mensaje a ServerProcess (asincrónicamente). El mensaje consta de un tuple % con el átomo "pausa" y el número "10". ServerProcess ! {}pausa, 10} % Recibir mensajes enviados a este proceso recibir a_message - algo; {}datos, DataContent} - mango()DataContent); {}Hola., Texto} - io:formato()"Got hello message: ~", [Texto]); {}Adiós., Texto} - io:formato()"Mensaje de despedida: ~", [Texto]) final.

Como muestra el ejemplo, los procesos se pueden crear en nodos remotos y la comunicación con ellos es transparente en el sentido de que la comunicación con los procesos remotos funciona exactamente como la comunicación con los procesos locales.

La simultaneidad es compatible con el método principal de manejo de errores en Erlang. Cuando un proceso se bloquea, sale ordenadamente y envía un mensaje al proceso de control que luego puede tomar medidas, como iniciar un nuevo proceso que se hace cargo de la tarea del proceso anterior.

Implementación

La implementación de referencia oficial de Erlang utiliza BEAM. BEAM está incluido en la distribución oficial de Erlang, denominada Erlang/OTP. BEAM ejecuta el código de bytes que se convierte en código enhebrado en el momento de la carga. También incluye un compilador de código nativo en la mayoría de las plataformas, desarrollado por High Performance Erlang Project (HiPE) en la Universidad de Uppsala. Desde octubre de 2001, el sistema HiPE está totalmente integrado en el sistema Open Source Erlang/OTP de Ericsson. También admite la interpretación, directamente desde el código fuente a través del árbol de sintaxis abstracta, a través de un script a partir de la versión R11B-5 de Erlang.

Módulos y carga de código activo

Erlang es compatible con la actualización dinámica de software a nivel de idioma. Para implementar esto, el código se carga y administra como "módulo" unidades; el módulo es una unidad de compilación. El sistema puede mantener dos versiones de un módulo en la memoria al mismo tiempo, y los procesos pueden ejecutar simultáneamente el código de cada uno. Las versiones se denominan "nuevas" y el "viejo" versión. Un proceso no pasará a la nueva versión hasta que haga una llamada externa a su módulo.

Un ejemplo del mecanismo de carga de código activo:

 Un proceso cuyo único trabajo es mantener un contador. Primera versión -módulo()contra). -Exportación[Empieza/0, codeswitch/1]). Empieza() - bucle()0). bucle()Sum) - recibir {}aumento, Conde} - bucle()Sum+Conde); {}contra, Pid} - Pid ! {}contra, Sum} bucle()Sum); code_switch - ?MODULO:codeswitch()Sum) % Forzar el uso de 'codeswitch/1' de la última versión MODULE final. codeswitch()Sum) - bucle()Sum).

Para la segunda versión, agregamos la posibilidad de restablecer el conteo a cero.

 Segunda versión -módulo()contra). -Exportación[Empieza/0, codeswitch/1]). Empieza() - bucle()0). bucle()Sum) - recibir {}aumento, Conde} - bucle()Sum+Conde); reseteo - bucle()0); {}contra, Pid} - Pid ! {}contra, Sum} bucle()Sum); code_switch - ?MODULO:codeswitch()Sum) final. codeswitch()Sum) - bucle()Sum).

Solo cuando se recibe un mensaje que consiste en el átomo code_switch, el bucle ejecutará una llamada externa a codeswitch/1 (?MODULE es una macro de preprocesador para el módulo actual). Si hay una nueva versión del módulo contador en la memoria, se llamará a su función codeswitch/1. La práctica de tener un punto de entrada específico en una nueva versión le permite al programador transformar el estado a lo que se necesita en la versión más nueva. En el ejemplo, el estado se mantiene como un número entero.

En la práctica, los sistemas se construyen utilizando los principios de diseño de Open Telecom Platform, lo que conduce a más diseños actualizables de código. La carga exitosa de código caliente es exigente. El código debe escribirse con cuidado para hacer uso de las instalaciones de Erlang.

Distribución

En 1998, Ericsson lanzó Erlang como software gratuito y de código abierto para garantizar su independencia de un único proveedor y aumentar el conocimiento del idioma. Erlang, junto con las bibliotecas y la base de datos distribuida en tiempo real Mnesia, forma la colección de bibliotecas de OTP. Ericsson y algunas otras empresas respaldan comercialmente a Erlang.

Desde el lanzamiento del código abierto, Erlang ha sido utilizado por varias empresas en todo el mundo, incluidas Nortel y T-Mobile. Aunque Erlang fue diseñado para llenar un nicho y se ha mantenido como un lenguaje oscuro durante la mayor parte de su existencia, su popularidad está creciendo debido a la demanda de servicios simultáneos. Erlang ha encontrado algún uso en el campo de servidores de juegos de rol en línea multijugador masivo (MMORPG).