Monografias.com > Computación > Programación
Descargar Imprimir Comentar Ver trabajos relacionados

Lenguaje Funcional Miranda




Enviado por dpalomo



Partes: 1, 2

    1. Introducción a la
      programación funcional.
    2. Lenguajes de programación
      ¿Por qué hay tantos y aparecen
      nuevos?
    3. Evolución de los
      lenguajes funcionales
    4. Un lenguaje funcional avanzado:
      Miranda
    5. Perspectiva
      histórica de los lenguajes de
      programación.
    6. Criterios de
      evaluación de los LP
    7. Modelo de
      compilación: análisis
    8. Generación
      de código intermedio
    9. Generación
      del código objeto
    10. Gramáticas
      de contexto libre
    11. Glosario
    12. Índice
      histórico de los lenguajes
      funcionales
    13. Bibliografía

    INTRODUCCIÓN

    MIRANDA

    1985

    MIRANDA es un lenguaje
    funcional propuesto por D. Turner en 1985. Este mismo autor
    había desarrollado anteriormente otros lenguajes del mismo
    tipo: SASL (1976) y KRC (1981). HASKELL es un sucesor de
    MIRANDA.

    Todos los lenguajes de la familia de
    MIRANDA se caracterizan porque los argumentos se pasan a las
    funciones sin
    evaluar (lazy evaluation): el argumento de una función se
    evalúa cuando la función necesita su valor.

    Un programa en este
    tipo de lenguajes consiste en un conjunto de declaraciones de
    ecuaciones
    recursivas.

    Objetivos

    El paradigma
    de programación
    funcional
    es uno de los fundamentales entre los
    llamados de programación
    declarativa
    . Como tal, permite aunar los
    componentes de especificación y programación en las tareas de
    solución automática de problemas.
    Los lenguajes funcionales ofrecen al programador un buen
    número de recursos
    expresivos que permiten resolver problemas
    complejos
    mediante programas
    pequeños y robustos. Entre ellos cabe destacar: un
    sistema de
    tipos polimórficos que permite definir una amplia variedad
    de estructuras de
    datos de uso
    genérico, la posibilidad de definir funciones que aceptan
    otras funciones como argumentos y devuelven funciones como
    resultado, facilidades para definir y manipular estructuras de
    datos infinitas, un modelo
    computacional simple, claro y bien fundamentado, etc. De no menor
    importancia es la posibilidad de razonar, de forma sencilla,
    acerca de las propiedades de los programas: su corrección,
    su eficacia, su
    comportamiento
    en ejecución, … Esto permite optimizar las tareas de
    implementación de los lenguajes funcionales.
    Podemos encontrar, en casi todos los lenguajes de programación
    funcional
    , un núcleo común de
    conceptos y técnicas
    asentado sobre bases firmemente establecidas. En esta asignatura
    estudiamos los fundamentos de la programación
    funcional
    y su utilización en la
    definición de implementaciones correctas y eficientes de
    los lenguajes de
    programación que se enmarcan en este
    paradigma.

    LENGUAJES DE
    PROGRAMACIÓN
    ¿POR QUÉ HAY TANTOS Y
    APARECEN NUEVOS?

    Por: Hanna Oktaba

    La computadora, a
    diferencia de otras herramientas
    que en general apoyan el esfuerzo físico de los humanos,
    fue inventada para facilitar el trabajo
    intelectual. Si el hombre
    tiene algún problema, por ejemplo "sumar dos y dos", el
    diseñador define el algoritmo que
    resuelve el problema, el programador lo codifica en un lenguaje de
    programación, el cual la computadora
    es capaz de "entender", luego la computadora ejecuta el
    algorítmo expresado como programa en el lenguaje de
    programación en cuestión, y listo. La
    máquina le entrega al hombre la
    respuesta "4", sin que éste tuviera que esforzar sus
    neuronas.

    ¿Cuál es el papel del lenguaje de
    programación en este proceso? Es
    muy importante, el lenguaje de programación es el medio de
    comunicación entre el hombre y la
    máquina. El modelo general de las computadoras,
    desde que fue esbozado por von Neumann,
    no ha cambiado mucho, mientras que la invención humana
    para proponerse nuevos problemas a resolver, usando la
    computadora, parece no tener límites.
    En consecuencia, los lenguajes de programación tienen que
    adaptarse a éstas crecientes necesidades y aumentar la
    expresividad para poder resolver
    problemas muy diversos y cada vez más complejos.
    Además, tienen que ofrecer cierta eficiencia en la
    ejecución. Es un logro difícil de alcanzar y por lo
    tanto, se requiere una búsqueda constante de nuevos
    lenguajes para ello.

    Permítanme exponer un breve panorama de los
    más importantes tipos de lenguajes de
    programación.

    Lenguajes Imperativos
    Comenzaré por los llamados lenguajes imperativos. En este
    tipo de lenguajes, cuyo origen está ligado a la propia
    arquitectura
    de von Neumann, la arquitectura consta de una secuencia de
    celdas, llamadas memoria, en la
    cual se pueden guardar en forma codificada, lo mismo datos que
    instrucciones; y de un procesador, el
    cual es capaz de ejecutar de manera secuencial una serie de
    operaciones,
    principalmente aritméticas y booleanas, llamadas comandos. En
    general, un lenguaje imperativo ofrece al programador conceptos
    que se traducen de forma natural al modelo de la
    máquina.

    Los lenguajes imperativos más destacados de la
    historia han
    sido: FORTRAN, Algol, Pascal, C,
    Modula-2, Ada. Seguramente, los lectores conocen por lo menos uno
    de ellos.

    El programador, al utilizar un lenguaje imperativo, por
    lo general tiene que traducir la solución abstracta del
    problema a términos muy primitivos, cercanos a la
    máquina. La distancia entre el nivel del razonamiento
    humano y lo expresable por los lenguajes imperativos causa que
    sus programas sean más "comprensibles" para la
    máquina que para el hombre. Esta desventaja para nosotros,
    reflejada en la dificultad que tenemos al construir programas en
    un lenguaje imperativo, se vuelve una ventaja en el momento de la
    generación del código.
    El programa está expresado en términos tan cercanos
    a la máquina, que el código generado es
    relativamente parecido al programa original, lo que permite
    cierta eficiencia en la ejecución.

    Lenguajes Funcionales
    Los matemáticos desde hace un buen tiempo
    están resolviendo problemas usando el concepto de
    función. Una función convierte ciertos datos en
    resultados. Si supiéramos cómo evaluar una
    función, usando la computadora, podríamos resolver
    automáticamente muchos problemas. Así pensaron
    algunos matemáticos, que no le tenían miedo a la
    máquina, e inventaron los lenguajes de programación
    funcionales. Además, aprovecharon la posibilidad que
    tienen las funciones para manipular datos simbólicos, y no
    solamente numéricos, y la propiedad de
    las funciones que les permite componer, creando de esta manera,
    la oportunidad para resolver problemas complejos a partir de las
    soluciones a
    otros más sencillos. También se incluyó la
    posibilidad de definir funciones recursivamente.

    Un lenguaje funcional ofrece conceptos que son muy
    entendibles y relativamente fáciles de manejar para todos
    los que no se durmieron en las clases de matemáticas. El lenguaje funcional
    más antiguo, y seguramente el más popular hasta la
    fecha, es LISP, diseñado por McCarthy [1]
    en la segunda mitad de los años 50. Su área de
    aplicación es principalmente la Inteligencia
    Artificial. En la década de los 80 hubo una nueva ola
    de interés
    por los lenguajes funcionales, añadiendo la
    tipificación y algunos conceptos modernos de
    modularización y polimorfismo, como es el caso del
    lenguaje ML.

    Programar en un lenguaje funcional significa construir
    funciones a partir de las ya existentes. Por lo tanto es
    importante conocer y comprender bien las funciones que conforman
    la base del lenguaje, así como las que ya fueron definidas
    previamente. De esta manera se pueden ir construyendo
    aplicaciones cada vez más complejas. La desventaja de este
    modelo es que resulta bastante alejado del modelo de la
    máquina de von Neumann y, por lo tanto, la eficiencia de
    ejecución de los intérpretes de lenguajes
    funcionales no es comparable con la ejecución de los
    programas imperativos precompilados. Para remediar la
    deficiencia, se está buscando utilizar arquitecturas
    paralelas que mejoren el desempeño de los programas funcionales, sin
    que hasta la fecha estos intentos tengan un impacto real
    importante.

    Lenguajes Lógicos
    Otra forma de razonar para resolver problemas en
    matemáticas se fundamenta en la lógica
    de primer orden. El
    conocimiento básico de las matemáticas se puede
    representar en la lógica en forma de axiomas, a los cuales
    se añaden reglas formales para deducir cosas verdaderas
    (teoremas) a partir de los axiomas. Gracias al trabajo de
    algunos matemáticos, de finales de siglo pasado y principios de
    éste, se encontró la manera de automatizar
    computacionalmente el razonamiento lógico
    –particularmente para un subconjunto significativo de la
    lógica de primer orden– que permitió que la
    lógica
    matemática diera origen a otro tipo de lenguajes de
    programación, conocidos como lenguajes lógicos.
    También se conoce a estos lenguajes, y a los funcionales,
    como lenguajes declarativos, porque el programador, parar
    solucionar un problema, todo lo que tiene que hacer es
    describirlo vía axiomas y reglas de deducción en el caso de la
    programación lógica y vía funciones en el
    caso de la programación funcional.

    En los lenguajes lógicos se utiliza el formalismo
    de la lógica para representar el conocimiento
    sobre un problema y para hacer preguntas que, si se demuestra que
    se pueden deducir a partir del conocimiento dado en forma de
    axiomas y de las reglas de deducción estipuladas, se
    vuelven teoremas. Así se encuentran soluciones a problemas
    formulados como preguntas. Con base en la información expresada dentro de la
    lógica de primer orden, se formulan las preguntas sobre el
    dominio del
    problema y el intérprete del lenguaje lógico trata
    de encontrar la respuesta automáticamente. El conocimiento
    sobre el problema se expresa en forma de predicados (axiomas) que
    establecen relaciones sobre los símbolos que representan los datos del
    dominio del problema.

    El PROLOG es el primer lenguaje lógico y el
    más conocido y utilizado. Sus orígenes se remotan a
    los inicios de la década de los 70 con los trabajos del
    grupo de A.
    Colmerauer [2]
    en Marsella, Francia.
    También en este caso, las aplicaciones a la Inteligencia
    Artificial mantienen el lenguaje vivo y útil.

    En el caso de la programación lógica, el
    trabajo del programador se restringe a la buena descripción del problema en forma de hechos
    y reglas. A partir de ésta se pueden encontrar muchas
    soluciones dependiendo de como se formulen las preguntas (metas),
    que tienen sentido para el problema. Si el programa está
    bien definido, el sistema encuentra automáticamente las
    respuestas a las preguntas formuladas. En este caso ya no es
    necesario definir el algoritmo de solución, como en la
    programación imperativa, en cambio, lo
    fundamental aquí es expresar bien el conocimiento sobre el
    problema mismo. En programación lógica, al igual
    que en programación funcional, el programa, en este caso
    los hechos y las reglas, están muy alejados del modelo von
    Neumann que posee la máquina en la que tienen que ser
    interpretados; por lo tanto, la eficiencia de la ejecución
    no puede ser comparable con la de un programa equivalente escrito
    en un lenguaje imperativo. Sin embargo, para cierto tipo de
    problemas, la formulación del programa mismo puede ser
    mucho más sencilla y natural (para un programador
    experimentado, por supuesto).

    Lenguajes Orientados a Objetos
    A mediados de los años 60 se empezó a vislumbrar el
    uso de las computadoras para la simulación
    de problemas del mundo real. Pero el mundo real está lleno
    de objetos, en la mayoría de los casos complejos, los
    cuales difícilmente se traducen a los tipos de datos
    primitivos de los lenguajes imperativos. Así es que a dos
    noruegos, Dahl y Nygaard [3],
    se les ocurrió el concepto de objeto y sus
    colecciones, llamadas clases de objetos, que permitieron
    introducir abstracciones de datos a los lenguajes de
    programación. La posibilidad de reutilización del
    código y sus indispensables modificaciones, se reflejaron
    en la idea de las jerarquías de herencia de
    clases
    . A ellos también les debemos el concepto de
    polimorfismo introducido vía procedimientos
    virtuales. Todos estos conceptos fueron presentados en el
    lenguaje Simula 67, desde el año 1967. Aunque pensado como
    lenguaje de propósito general, Simula tuvo su mayor
    éxito
    en las aplicaciones de simulación discreta, gracias a la
    clase
    SIMULATION que facilitaba considerablemente la
    programación.

    La comunidad
    informática ha tardado demasiado en
    entender la utilidad de los
    conceptos básicos de Simula 67, que hoy identificamos como
    conceptos del modelo de objetos. Tuvimos que esperar hasta los
    años 80 para vivir una verdadera ola de propuestas de
    lenguajes de programación con conceptos de objetos
    encabezada por Smalltalk, C++, Eiffel, Modula-3, Ada 95 y
    terminando con Java. La moda de objetos
    se ha extendido de los lenguajes de programación a la
    Ingeniería de Software. Si alguien
    desarrolla sistemas y
    todavía no sabe qué es un objeto, mejor que no lo
    confiese en público y lea cuanto antes los números
    3, 4 y 20 de la revista
    Soluciones Avanzadas.

    El modelo de objetos, y los lenguajes que lo usan,
    parecen facilitar la construcción de sistemas o programas en
    forma modular. Los objetos ayudan a expresar programas en
    términos de abstracciones del mundo real, lo que aumenta
    su comprensión. La clase ofrece cierto tipo de
    modularización que facilita las modificaciones al sistema.
    La reutilización de clases previamente probadas en
    distintos sistemas también es otro punto a favor. Sin
    embargo, el modelo de objetos, a la hora de ser interpretado en
    la arquitectura von Neumann conlleva un excesivo manejo
    dinámico de memoria debido a la constante creación
    de objetos, así como a una carga de código fuerte
    causada por la constante invocación de métodos.
    Por lo tanto, los programas en lenguajes orientados a objetos
    siempre pierden en eficiencia, en tiempo y memoria, contra los
    programas equivalentes en lenguajes imperativos. Para
    consolarnos, los expertos dicen que les ganan en la
    comprensión de código.

    Lenguajes Concurrentes, Paralelos y
    Distribuidos

    La necesidad de ofrecer concurrencia en el acceso a los recursos
    computacionales se remonta a los primeros sistemas
    operativos. Mientras que un programa realizaba una
    operación de entrada o salida otro podría gozar del
    tiempo del procesador para sumar dos números, por ejemplo.
    Aprovechar al máximo los recursos computacionales fue una
    necesidad apremiante, sobre todo en la época en que las
    computadoras eran caras y escasas; el sistema operativo
    tenía que ofrecer la ejecución concurrente y segura
    de programas de varios usuarios, que desde distintas terminales
    utilizaban un solo procesador, y así surgió la
    necesidad de introducir algunos conceptos de
    programación concurrente para programar los
    sistemas
    operativos.

    Posteriormente, cuando los procesadores
    cambiaron de tamaño y de precio, se
    abrió la posibilidad de contar con varios procesadores en
    una máquina y ofrecer el procesamiento en
    paralelo
    , es decir, procesar varios programas al mismo
    tiempo. Esto dio el impulso a la creación de lenguajes que
    permitían expresar el paralelismo. Finalmente, llegaron
    las redes de
    computadoras, que también ofrecen la posibilidad de
    ejecución en paralelo, pero con procesadores distantes, lo
    cual conocemos como la programación
    distribuida
    .

    En resumen, el origen de los conceptos para el manejo de
    concurrencia, paralelismo y distribución está en el deseo de
    aprovechar al máximo la arquitectura von Neumann y sus
    modalidades reflejadas en conexiones paralelas y
    distribuidas.

    Históricamente encontramos en la literatura soluciones
    conceptuales y mecanismos tales como: semáforos, regiones
    críticas, monitores,
    envío de mensajes (CSP), llamadas a procedimientos remotos
    (RPC), que posteriormente se incluyeron como partes de los
    lenguajes de programación en Concurrent Pascal, Modula,
    Ada, OCCAM, y últimamente en Java.

    Uno de los ejemplos más importantes es el modelo
    de envío de mensajes de CSP de Hoare [4],
    para las arquitecturas paralelas y distribuidas, el cual no
    solamente fructificó en una propuesta del lenguaje de
    programación OCCAM, sino dio origen a una nueva familia de
    procesadores, llamados "transputers", que fácilmente se
    componen en una arquitectura paralela.

    Es difícil evaluar las propuestas existentes de
    lenguajes para la programación concurrente, paralela y
    distribuida. Primero, porque los programadores están
    acostumbrados a la programación secuencial y cualquier uso
    de estos mecanismos les dificulta la construcción y el
    análisis de programas. Por otro lado, este
    tipo de conceptos en el pasado fue manejado principalmente a
    nivel de sistemas operativos, protocolos de
    comunicación, etcétera, donde la eficiencia era
    crucial, y por lo tanto no se utilizaban lenguajes de alto nivel
    para la programación. Hoy en día, la
    programación de sistemas complejos tiene que incluir las
    partes de comunicaciones, programación distribuida y
    concurrencia. Esto lo saben los creadores de los lenguajes
    más recientes, que integran conceptos para manejar: los
    hilos de control,
    comunicación, sincronización y no determinismo; el
    hardware y las
    aplicaciones se los exigen.

    A manera de conclusiones
    A quien pensaba que en la materia de los
    lenguajes de programación ya se había dicho todo,
    espero haberlo convencido de que estaba equivocado. Como dice
    Hoare [4]:
    "El diseño
    de un lenguaje de programación es siempre un compromiso,
    en el cual un buen diseñador tiene que tomar en cuenta: el
    nivel de abstracciones deseado, la arquitectura del
    hardware, y el rango propuesto de las
    aplicaciones".

    Lo malo, o lo bueno, según lo veamos, es que
    queremos construir sistemas a niveles cada vez más
    abstractos, con hardware cada vez más complicado
    y con aplicaciones cada vez más ambiciosas (véase
    la reciente efervescencia con Java y sus aplicaciones en Internet). Por lo tanto, el
    trabajo para los diseñadores de lenguajes existe para un
    buen rato.

    Una versión completa de este
    artículo aparecerá en un número
    próximo a salir de la revista Soluciones Avanzadas,
    dedicado a los lenguajes de
    programación
    .

    EVOLUCIÓN
    DE LOS LENGUAJES FUNCIONALES

    Introducción

    ¿Por qué surge el modelo
    funcional?

    Programación
    Funcional

    En programación funcional, un
    programa consta de:

    • un conjunto de operaciones primitivas cuyo
      significado está predeterminado en el sistema. Por
      ejemplo, la suma de números enteros, la resta, el
      producto,
      etc.
    • un conjunto de definiciones de función,
      establecidas por el programador, que eventualmente
      emplearán las operaciones primitivas. Por ejemplo, la
      función factorial.
    • un dato de entrada (entendido como la
      aplicación de una de las funciones definidas sobre otros
      datos). Por ejemplo: fact(2*fact(2)).

    La ejecución del programa funcional consiste en
    el cálculo
    del valor asociado al dato de entrada de acuerdo con las
    definiciones dadas para las funciones en el programa. El proceso
    de cálculo de dicho valor se conoce como
    evaluación
    del dato de entrada.
    Dicha evaluación
    puede realizarse de muchas formas, pero hay dos estrategias
    fundamentales para llevarla a cabo: la estrategia
    voraz
    (eager) y la estrategia perezosa
    (lazy). La elección de una u otra tiene importantes
    repercusiones en la implementación y en el comportamiento
    operacional
    del proceso de
    evaluación
    .

    Problemas del modelo imperativo

    Los programas escritos en lenguajes de
    programación tradicionales como Pascal, C, ADA, etc.
    forman una abstracción de la máquina de Von-Neumann
    caracterizada por:

    • Memoria Principal para almacenamiento de datos y código
      máquina.
    • Unidad Central de Proceso con una serie de
      registros de
      almacenamiento temporal y un conjunto instrucciones de
      cálculo aritmético, modificación de
      registros y acceso a la Memoria
      Principal.

    Los programas imperativos están formados por una
    serie de datos globales y un conjunto de instrucciones ó
    código. Estos dos elementos forman una abstracción
    de los datos y código de la memoria principal.

    El programador trabaja en un nivel cercano a la
    máquina lo que le permite generar programas eficientes.
    Con esta proximidad aparece, sin embargo, una dependencia entre
    el algoritmo y la arquitectura que impide, por ejemplo, utilizar
    algoritmos
    programados para arquitecturas secuenciales en arquitecturas
    paralelas.

    Los algoritmos escritos en lenguajes imperativos se
    expresan mediante una secuencia de instrucciones que
    modifican el estado de un programa accediendo a los datos
    globales de la memoria. En este punto es donde empiezan los
    problemas:

    Ejemplo

    Program prueba;

    var flag:boolean;

    function f (n: integer):integer;

    begin

    flag:=not flag;

    if flag then f:=n;

    else f:=2*n;

    end;

    ……..

    –Programa principal

    begin

    flag:=true;

    ……

    write(f(1)); ß retorna 2

    write(f(1)); ß retorna 1

    …….

    write(f(1) + f(2)); ß retorna 4

    write(f(2) + f(1)); ß retorna 5

    En el primer caso la expresión f(1) retorna
    valores
    diferentes dependiendo de la secuencia de ejecución y en
    el segundo no se cumplen propiedades matemáticas simples
    tales como la conmutatividad. Estos ejemplos ponen en evidencia
    que ciertas características de los lenguajes imperativos
    tales como la asignación pueden traer consigo efectos
    laterales inesperados que oscurecen la semántica del programa; en consecuencia se
    hace difícil demostrar que los programas cumplen con los
    requerimientos especificados y que estén libres de
    errores.

    Este y otros problemas son inherentes al modelo
    computacional utilizado, por ende una solución factible de
    ser aplicada puede ser cambiar el modelo computacional. Entre
    otras alternativas se encuentran el modelo funcional o
    aplicativo
    cuyo objetivo es
    describir los problemas mediante funciones matemáticas sin
    efectos laterales, y el modelo lógico o declarativo que
    describe los problemas mediante relaciones entre objetos o
    entidades.

    Evolución del paradigma
    funcional

    Lambda calculo

    Los orígenes teóricos del modelo funcional
    se remontan a la década del 30, mas precisamente al
    año 1934, cuando Alonzo Church introdujo un modelo
    matemático de computación llamado lambda calculo. A pesar
    de que en esta época las computadoras aun no
    existían el lambda calculo se puede considerar como el
    primer lenguaje funcional de la historia y sus fundamentos fueron
    la base de toda la teoría
    de la programación funcional y de los lenguajes
    funcionales desarrollados posteriormente. Se puede decir que los
    lenguajes funcionales modernos son versiones de lambda calculo
    con numerosas ayudas sintácticas.

    La principal característica de lambda calculo es
    su simplicidad ya que permite efectuar solo dos
    operaciones:

    • Definir funciones de un solo argumento y con un
      cuerpo especifico, denotado por la siguiente
      terminología: l x.B , en donde x determina el
      parámetro o argumento formal y B representa el cuerpo de
      la función, es decir f(x) = B.
    • Aplicar alguna de las funciones definidas sobre un
      argumento real (A); lo que es conocido también con el
      nombre de reducción, y que no es otra cosa que sustituir
      las ocurrencias del argumento formal (x), que aparezcan en el
      cuerpo (B) de la función, con el argumento real(A), es
      decir: ( l x.B
      ) A.

    Ejemplo:

    ( l x. (x+5) ) 3 , lo que
    indica que en la expresión x+5, se debe sustituir el
    valor de x por 3.

    Cuando ya no es posible reducir una función, se
    dice que ésta se encuentra en estado normal,
    es decir se obtiene el valor de la función. Este
    último, siempre dependerá únicamente de los
    argumentos y siempre tendrá la consistencia de retornar el
    mismo valor para los mismos argumentos. Por lo tanto se tiene
    transparencia referencial.

    Los dos mecanismos básicos presentados
    anteriormente se corresponden con los conceptos de
    abstracción funcional y aplicación de
    función
    ; si le agregamos un conjunto de
    identificadores para representar variables se obtiene lo
    mínimo necesario para tener un lenguaje de
    programación funcional. Lambda calculo tiene el mismo
    poder computacional que cualquier lenguaje imperativo
    tradicional.

    Las ventajas de tener un lenguaje tan simple
    son:

    • Permite definiciones simples.
    • Facilita el estudio de aspectos
      computacionales.
    • Su carácter formal facilita la
      demostración de propiedades.

    Aplicaciones

    • Compilación de lenguajes
      funcionales.
    • Especificar semántica a lenguajes
      imperativos.
    • Formalismo para definir otras teorías.

    MIRANDA

    Fue creado por Turner en 1986. Es similar a ML dado que
    utiliza una sintaxis similar, el alcance es estático, es
    fuertemente para prototipado, y usa el mismo método de
    inferencia de tipos.

    Objetivo: Facilitar la tarea del programador
    incorporando facilidades sintácticas como las guardas,
    pattern matching y las listas por comprensión.

    Características:

    • Todos los lenguajes de la familia de Miranda se
      caracterizan porque los argumentos se pasan a las funciones
      sin evaluar (lazy evaluation).
    • Recursión.

    Miranda es un sistema de la programación
    funcional avanzado bajo que corre el sistema operativo de
    UNIX (*). El
    objetivo del sistema de Miranda es a proporcione un idioma
    funcional moderno, incluido en un ` industrial la calidad'
    programando el ambiente.
    Está usándose ahora a un crecimiento

    el número de sitios por enseñar la
    programación funcional y como un vehículo para el
    prototyping rápido de software. El
    propósito de este artículo corto es para dar una
    apreciación global breve de los rasgos principales de
    Miranda. Los temas nosotros discutirá, en el orden,
    es:

    Las ideas básicas

    El Miranda que programa el ambiente

    Las ecuaciones defendidas y estructura del
    bloque

    El modelo emparejando

    Zurrando y las funciones del orden más
    altas

    Liste las comprensiones

    La evaluación
    perezosa y listas del infinito

    Polymorphic muy bien la mecanografía

    El usuario definió los tipos

    Teclee los sinónimos

    Los tipos de los datos abstractos

    La recopilación separada y
    uniéndose

    El estado de aplicación actual

    (*) La nota: UNIX es una marca de
    fábrica de AT&T Campanilla Laboratorios, Miranda es
    un

    la marca de fábrica de Software de la Investigación S.A..

    Las Ideas básicas

    El Miranda que programa el idioma es completamente
    funcional – hay no efectos del lado o los rasgos indispensables
    de cualquier amable. Un programa (realmente nosotros no lo llame
    un programa, nosotros lo llamamos una "escritura") es
    una colección de ecuaciones que definen las varias
    funciones y el datos estructura que nosotros somos interesado
    computando. El orden en que las ecuaciones se dan es no en
    general significante. No hay ninguna obligación por
    ejemplo para el la definición de una entidad para preceder
    su primer uso. Aquí es un muy simple el ejemplo de una
    escritura de Miranda:

    z = el sq x / el sq y

    el sq n = n * n

    x = un + b

    y = un – b

    un = 10

    b = 5

    Note la ausencia de equipaje sintáctico – Miranda
    es, por el plan, más
    bien, conciso. No hay ninguna declaración del tipo
    obligatoria, aunque (vea después) el idioma se teclea
    fuertemente. No hay ningún punto y coma al final de las
    definiciones – el algoritmo del análisis gramatical hace
    uso inteligente de diseño.

    La nota que la anotación para la
    aplicación de la función simplemente es la
    yuxtaposición, como en el "sq x". En la definición
    de la función del sq, "n" está un formal el
    parámetro – su alcance se limita a la ecuación en
    que ocurre (considerando que los otros nombres introdujeron sobre
    tiene la escritura entera para su alcance).

    La estructura de los datos normalmente usada es la lista
    que en Miranda es escrito con los anaqueles del cuadrado y comas,
    por ejemplo:

    el week_days = ["Mon", "Tue", se "Casan", "Thur",
    "Fri"]

    días = el week_days ++ [se "Sentaba",
    Sol]

    Las listas pueden añadirse por el "++" operador.
    Otros funcionamientos útiles en las listas incluyen el
    infijo ": " qué prefijos un elemento al frente de un
    liste, "#" qué toma la longitud de una lista, y clava "! "
    qué hace

    el subscripting. Así por ejemplo 0:[1,2,3] tiene
    el valor [0,1,2,3], #días es 7, y el days!0 es
    "Mon."

    Hay también un operador "–" qué lista la
    substracción. Por ejemplo

    [1,2,3,4,5]–[2,4] es [1,3,5].

    Hay una anotación de la taquigrafía que usa ".. " para las listas
    cuyo el formulario de los elementos

    una serie aritmética. Por ejemplo aquí son
    las definiciones del factorial funcione, y de un resultado del
    número que es la suma de los números impares entre
    1 y 100 (la suma y producto son la biblioteca
    funciona):

    el fac n = el producto [1.. n]

    el resultado = la suma [1,3 ..100]

    Los elementos de una lista deben todos sea del mismo
    tipo. Una sucesión de se llaman elementos de tipo mixto un
    tuple, y es el usando escrito los paréntesis en lugar de
    los anaqueles del cuadrado. El ejemplo

    el empleado = ("Jones",True,False,39)

    Tuples son análogos a los archivos en
    Pascal (considerando que las listas son análogas a las
    series). Tuples no puede ser los subscripted – sus elementos se
    extraen por modelo que empareja (vea después).

    El Ambiente de Miranda

    El sistema de Miranda es interactivo y corre bajo UNIX
    como un ego el subsistema contenido. La acción
    básica es evaluar las expresiones, proporcionado por el
    usuario en el término, en el ambiente establecido
    por,

    la escritura actual. "Z" por ejemplo evaluando en el
    contexto del primero escritura dada sobre produciría el
    resultado "9."

    El recopilador de Miranda trabaja junto con un editor
    (normalmente esto es el "vi" pero puede ponerse a cualquier
    editor de la opción de los usuarios) y escrituras es
    automáticamente los recompiled después revisa, y
    cualquier sintaxis o errores del tipo el signaled inmediatamente.
    Los polymorphic teclean el sistema permite un alto la
    proporción de errores lógicos ser descubierto a
    compila tiempo.

    Hay una biblioteca grande real de funciones normales.
    Hay un manual de la
    referencia en línea que documenta todos los aspectos del
    sistema. Hay una interfaz buena a UNIX, permitiendo Miranda
    programa para tomar los datos de, y envía los datos a,
    UNIX arbitrario archiva, y también es posible a invoque
    Miranda programa directamente de la cáscara de UNIX, y
    para combinar ellos, vía las cañerías de
    UNIX, con procesos
    escritos en otros idiomas.

    Las Ecuaciones defendidas y Estructura del
    Bloque

    Una ecuación puede tener varios lados de la mano
    derecha alternativos distinguidos por guardias – el guardia es
    escrito en lo siguiente correcto una coma. Para ejemplo que la
    más gran función del divisor común puede
    escribirse:

    el gcd un b = el gcd (un-b) b, si el a>b

    = el gcd un (b-un), si un <b

    = un, si el a=b

    El último guardia en tal una serie de
    alternativas puede escribirse

    "Por otra parte", en lugar de "Si la condición",
    para indicar un caso predefinido (*).

    También se permitía introducir las
    definiciones locales en la mano derecha el lado de una
    definición, por medio de un "donde" la cláusula.
    Considere para el ejemplo la definición siguiente de una
    función por resolver cuadrático las ecuaciones (o
    falla o ingresos una
    lista de una o dos raíces reales):

    el quadsolve un b c = el error las raíces"
    "complejas, si el delta <0

    = [- el b/(2*a)], si el delta=0

    = [- el b/(2*a) + el radix/(2*a),

    – el b/(2*a) – el radix/(2*a)], si el
    delta>0

    donde

    el delta = el b*b – 4*a*c

    el radix = el delta del sqrt

    Donde las cláusulas pueden ocurrir anidado, a la
    profundidad arbitraria, permitiendo Miranda los programas ser
    organizado con una estructura del bloque anidada. El sangrado de
    los bloques internos son compulsivos, cuando la
    información del diseño es usada por el
    parser.

    (*) La nota: Para la compatibilidad con las versiones
    más tempranas de Miranda el uso de

    la palabra "si" en guardias es optativo.

    El modelo Emparejando

    Se permitía definir una función dando
    varios alternativa las ecuaciones, distinguió por el uso
    de modelos
    diferentes en el formal los parámetros. Esto proporciona
    otro método de embale el análisis que

    es a menudo más elegante que el uso de guardias.
    Nosotros aquí damos algún simple los ejemplos de
    modelo que empareja en los números naturales, listas, y
    tuples.

    Aquí es (otro) la definición de la
    función factorial, y una definición de la
    función de Ackermann:

    fac 0 = 1

    el fac (el n+1) = (el n+1)*fac n

    el ack 0 n = el n+1

    el ack (el m+1) 0 = el ack m 1

    el ack (el m+1) (el n+1) = el m(ack del ack (el m+1)
    n)

    Aquí es un (ingenuo) la definición de una
    función por computar el no Fibonacci numeran:

    mienta 0 = 0

    mienta 1 = 1

    la mentirilla (el n+2) = la mentirilla (el n+1) + la
    mentirilla n

    Aquí son algunos ejemplos simples de funciones
    definidas el modelo emparejando en las listas:

    la suma [] = 0

    la suma (el a:x) = un + la suma x

    el producto [] = 1

    el producto (el a:x) = un * el producto x

    la marcha atrás [] = []

    la marcha atrás (el a:x) = x inverso ++
    [un]

    Accediendo los elementos de un tuple también se
    hace el modelo emparejando. Para

    ejemplo que la selección
    funciona en 2-tuples puede definirse así

    el fst (el a,b) = un

    el snd (el a,b) = b

    Como último ejemplos nosotros damos las
    definiciones de dos biblioteca de Miranda las funciones, toma y
    gota que el retorno los primeros miembros de n de una lista, y el
    resto de la lista sin los primeros miembros de n,
    respectivamente,

    tome 0 x = []

    tome (el n+1) [] = []

    tome (el n+1) (el a:x) = un: tome n x

    deje caer 0 x = x

    la gota (el n+1) [] = []

    la gota (el n+1) (el a:x) = la gota n x

    Aviso que las dos funciones se definen de tal una manera
    que que el la identidad
    siguiente siempre sostiene – "tome n x ++ la gota n x = x" –
    incluyendo en el caso patológico que la longitud de x
    está menos de n.

    Zurrando y las Funciones del Orden más
    Altas

    Miranda es un idioma del orden totalmente más
    alto – las funciones son primero la clase los ciudadanos y puede
    ser los dos pasados como los parámetros y puede volver
    como los resultados.

    La aplicación de la función queda
    asociativo, para que cuando nosotros le escribimos x y" a "f que
    es analizado como "(f x) y", significando que el resultado de
    aplicar f a x es un funcione que se aplica entonces a y. El
    lector puede probar fuera suyo entendiendo de funciones del orden
    más altas funcionando lo que es el valor de respuesta en
    la escritura siguiente:

    la respuesta = dos veces dos veces dos veces suc
    0

    dos veces f x = f (f x)

    el suc x = x + 1

    Note eso en Miranda que cada función de dos o
    más argumentos realmente es una función del orden
    más alta. Esto es muy útil como él permite
    parcial la aplicación. Por ejemplo el "miembro" es una
    función de la biblioteca tal que el "miembro x un"
    pruebas si la
    lista x contiene el elemento un (volviendo Verdadero

    o Falso como apropiado). aplicando al miembro
    parcialmente nosotros podemos derivar muchos predicados
    útiles, como

    la vocal = el miembro ['un', 'e', 'i', 'o',
    'u']

    el dedo = el miembro [0', 1', 2', 3', 4', 5', 6', 7',
    8', 9']

    mes = el miembro [el "Ene", el "Feb", "Mar", el "Abr",
    el "Jun", "jul", "ago", el "Sep",,

    "Oct", "Nov", el "Dic"]

    Como otro ejemplo de programación del orden
    más alta considere la función

    el foldr, definió

    el op del foldr k [] = k

    el op del foldr k (el a:x) = el op un (el op del foldr k
    x)

    Todas las funciones de proceso de lista normales pueden
    obtenerse parcialmente por el foldr aplicando. Los
    ejemplos

    la suma = el foldr (+) 0

    el producto = el foldr (*) 1

    la marcha atrás = el postfix del foldr
    []

    donde el postfix un x = x ++ [un]

    Liste las Comprensiones

    Las comprensiones de la lista dan una sintaxis concisa
    para una clase bastante general de las iteraciones encima de las
    listas. La sintaxis se adapta de una anotación
    análoga usado en la teoría fija (llamó la
    comprensión" fija). UN ejemplo simple de un la
    comprensión de la lista es:

    [el n*n | n <- [1 ..100]]

    Ésta es una lista que contiene (en el orden) los
    cuadrados de todos los números de 1 a 100. La
    expresión anterior se leería alto como la lista de
    todo el n*n tal ese n deducido de la lista 1 a 100". Nota que "n"
    es un local inconstante de la expresión anterior. La
    estructura inconstante-obligatoria al el derecho de la barra se
    llama un "generador" – el "<- " la señal denota eso la
    variable introdujo en sus rangos izquierdos encima de todos los
    elementos del liste en su derecho. El formulario general de una
    comprensión de la lista en Miranda

    es:

    [el cuerpo | los calificadores]

    donde cada calificador o es un generador, del var del
    formulario <- el exp, o el resto un filtro que es una
    expresión del boolean restringía los rangos de las
    variables
    introducidas por los generadores. Cuando dos o más los
    calificadores están presentes que ellos están
    separados por los puntos y comas. Un ejemplo de

    una comprensión de la lista con dos generadores
    es dada por lo siguiente la definición de una
    función por devolver una lista de todas las permutaciones
    de una lista dada,

    los permanente [] = [[]]

    los permanente x = [el a:y | un <- x; y <- los
    permanente (x–[un])]

    El uso de un filtro se muestra por la
    definición siguiente de una función qué toma
    un número e ingresos una lista de todos sus
    factores,

    los factores n = [i | i <- [1.. n div 2]; el mod de n
    i = 0]

    Liste a menudo que las comprensiones permiten
    concisión notable de expresión. Nosotros damos dos
    ejemplos. Aquí es una declaración de Miranda de
    Hoare

    El algoritmo de "Quicksort", como un método de
    ordenar una lista,

    la clase [] = []

    la clase (el a:x) = la clase [b | b <- x; b <=
    un]

    ++ [un] ++

    la clase [b | b <- x; el b>a]

    Luego es una solución de Miranda al ocho problema
    de las reinas. Nosotros tenemos a ponga a ocho reinas en los
    ajedreces que aborda para que ninguna reina dé el cheque a
    cualquiera otro. Desde que cualquier solución debe tener
    una reina exactamente en cada columna, un la
    representación conveniente para una tabla es una lista de
    enteros que dan la fila el número de la reina en cada
    columna sucesiva. En la escritura siguiente

    la función "corona n" devuelve todas las maneras
    seguras de poner a reinas adelante el primero las columnas de n.
    Una lista de todas las soluciones al ocho problema de las reinas
    es por consiguiente obtenido imprimiendo el valor de (corona
    8)

    reinas 0 = [[]]

    reinas (el n+1) = [el q:b | b <- corona n; q <- [0
    ..7]; q b seguro]

    q b seguro = y [~ verifica q b i | i <- [0.. #el
    b-1]]

    los cheques q b i
    = el q=b!i / el abs(q – el b!i)=i+1

    La Evaluación perezosa y Listas del
    Infinito

    El mecanismo de la evaluación de Miranda es
    "perezoso", en el sentido que no el subexpression se
    evalúa hasta que su valor se conozca para ser requerido.
    Uno la consecuencia de esto es que eso es posible definir
    funciones que son non-estricto (significando que ellos son
    capaces de devolver una respuesta aun cuando

    uno de sus argumentos es indefinido). por ejemplo
    nosotros podemos definir un la función condicional como
    sigue,

    el cond Verdadero x y = x

    el cond x y Falso = y

    y entonces lo usa en las tales situaciones como el "cond
    (el x=0) 0 (1/x) ".

    La otra consecuencia principal de evaluación
    perezosa es que lo hace posible apuntar definiciones de
    estructuras de los datos infinitas. Aquí es algunos
    ejemplos de definiciones de Miranda de listas infinitas (la nota
    que hay un formulario modificado del ".. " la anotación
    para la aritmética interminable las
    progresiones)

    unos = 1: unos

    repita un = x

    donde x = un: x

    el nats = [0..]

    las desigualdades = [1,3..]

    los cuadrados = [el n*n | n <- [0..]]

    los perfecto = [n | n <- [1..]; el sum(factors n) =
    n]

    imprima = el cedazo [2..]

    donde

    el cedazo (el p:x) = p: el cedazo [n | n <- x; el mod
    de n p> 0]

    Una aplicación interesante de listas infinitas es
    actuar como las mesas del lookup por esconder los valores de
    una función. Por ejemplo nuestro más temprano
    ingenuo La definición de mentirilla puede mejorarse de
    exponencial a lineal la complejidad cambiando el recursion para
    usar una mesa del lookup, así,

    mienta 0 = 1

    mienta 1 = 1

    la mentirilla (el n+2) = el flist!(n+1) + el
    flist!n

    donde

    el flist = la mentirilla del mapa [0..]

    Otro uso importante de listas infinitas es que ellos nos
    permiten que escribamos programas funcionales que representan
    redes de
    comunicar los procesos. Considere por ejemplo el Hamming numera
    el problema – nosotros tenemos que imprimir en el orden
    ascendente todos los números del formulario 2^a*3^b*5^c,
    para el a,b,c>=0.

    Hay una solución buena a este problema por lo que
    se refiere a comunicar procesos como que pueden expresarse en
    Miranda siguen el hamming = 1: una (f 2) (una (f 3) (f
    5))

    donde

    f un = [el n*a | n <- el hamming]

    una (el a:x) (el b:y) = un: fusione x (el b:y), si un
    <b

    = b: una (el a:x) y, si el a>b

    = un: fusione x y, por otra parte,

    Polimorfismo Strong la
    Mecanografía

    Miranda se teclea fuertemente. Es decir, cada
    expresión y cada el subexpression tiene un tipo a que
    puede deducirse compilar tiempo, y cualquiera la inconsistencia
    en la estructura del tipo de una escritura resulta en un compile
    cronometre el mensaje del error. Nosotros aquí resumimos
    la anotación de Miranda brevemente para

    sus tipos.

    Hay tres tipos primitivos, num llamados, bool, y trabajo
    por horas. El tipo el num comprende entero y los números
    del punto flotantes (la distinción entre los enteros y se
    ocupan de números del punto flotantes en momento de la
    carrera – esto no se considera como ser una distinción del
    tipo). hay dos valores de bool del tipo, llamado Verdadero y
    Falso. El trabajo por horas del tipo comprende el ascii el
    carácter puso – las constantes del carácter son
    escrito en las solas citas, mientras usando Las convenciones de
    escape de C, por ejemplo 'un', '$', ' n' etc.

    Si T es el tipo, entonces [T] es el tipo de listas cuyos
    elementos son de tipo T. por ejemplo [[1,2],[2,3],[4,5]] es de
    tipo [[el num]], eso es es un la lista de listas de
    números. Las constantes del cordón son de tipo [el
    trabajo por horas], de hecho un cordón como "hola"
    simplemente es una manera de la taquigrafía de escribir
    ['h', 'e', 'l', 'l', 'o'].

    Si T1 a Tn son los tipos, entonces (T1,…, Tn) es el
    tipo de tuplas con los objetos de estos tipos como los
    componentes. Por ejemplo (Verdadero, "hello",36) es de tipo (el
    bool,[char],num).

    Si T1 y T2 son los tipos, entonces T1->T2 es el tipo
    de una función con los argumentos en T1 y resultados en
    T2. Por ejemplo la suma de la función es de el tipo [el
    num]->num. El quadsolve de la función, dado antes, es
    de tipo num->num->num->[num]. la Nota que "-> " es
    correcto asociativo.

    Las escrituras de Miranda pueden incluir las
    declaraciones del tipo. Éstos son los usando escrito ":: "
    significar es de tipo. El ejemplo

    el sq:: el num -> el num

    el sq n = n * n

    La declaración del tipo no es necesaria, sin
    embargo. El recopilador siempre es capaz para deducir el tipo de
    un identificador de su ecuación definiendo. Las escrituras
    de Miranda contienen a menudo las declaraciones del tipo como
    éstos es útil para la documentación (y ellos proporcionan un
    cheque extra, desde el typechecker,

    quéjese si el tipo declarado es incoherente con
    los inferimos uno).

    Los tipos pueden ser los polymorphic, en el sentido de
    Milner [Milner 78]. Esto es indicado usando los símbolos *
    * * * * * el etc como un alfabeto de genérico teclee las
    variables. Por ejemplo, la función de identidad,
    definió en el La biblioteca de Miranda como

    el id x = x

    tiene el tipo siguiente

    el id:: * -> *

    esto significa que la función de identidad tiene
    muchos tipos. A saber todos aquéllos qué puede
    obtenerse sustituyendo un tipo arbitrario para el genérico
    el tipo inconstante, eg "num->num", "bool->bool", "(* ->
    * *) -> (* -> * *) " y para que en.

    Nosotros ilustramos los Miranda teclean el sistema dando
    los tipos para algunos del las funciones definieron hasta ahora
    en este artículo

    el fac:: el num -> el num

    el ack:: el num -> el num -> el num

    la suma:: [el num] -> el num

    mes:: [el trabajo por horas] -> el bool

    la marcha atrás:: [*] -> [*]

    el fst:: (*, * *) -> *

    el snd:: (*, * *) -> * *

    el foldr:: (* -> * * -> * *) -> * * -> [*]
    -> * *

    los permanente:: [*] -> [[*]]

    El usuario Definió los Tipos

    El usuario puede introducir los nuevos tipos. Esto se
    hace por una ecuación en ": := ". Por ejemplo un tipo de
    árboles
    binarios etiquetados (con numérico las etiquetas) se
    introduciría como sigue,

    el árbol: := Nilt | los num del Nodo obligan a
    refugiarse en un árbol el árbol

    Esto introduce tres nuevos identificadores –
    árbol de que es el nombre el teclee, y "Nilt" y "Nodo" que
    son los constructores para los árboles. Nilt es un
    constructor atómico, mientras el Nodo toma tres
    argumentos, de los tipos, mostrado. Aquí es un ejemplo de
    un árbol construyó usando a estos
    constructores

    el t1 = Nodo 7 (el Nodo 3 Nilt Nilt) (el Nodo 4 Nilt
    Nilt)

    Para analizar un objeto de usuario definido el tipo,
    nosotros usamos el modelo emparejando. Para el ejemplo
    aquí es una definición de una función por
    tomar la imagen del espejo
    de un árbol

    refleje Nilt = Nilt

    el espejo (el Nodo un x y) = el Nodo un (el espejo y)
    (el espejo x)

    El usuario definió los tipos pueden ser los
    polymorphic – esto se muestra introduciendo uno o variables del
    tipo más genéricas como los parámetros del
    ": := " la ecuación. Para el ejemplo nosotros podemos
    generalizar la definición de árbol para permitir
    arbitrario las etiquetas, así,

    el árbol *: := Nilt | el Nodo * (el árbol
    *) (el árbol *)

    esto introduce a una familia de tipos del árbol,
    incluso el num del árbol, el bool del árbol, el
    tree(char->char), y así sucesivamente. Los tipos
    introducidos por ": := " se llaman las definiciones los tipos"
    "algebraicos.

    Los tipos algebraicos son una idea muy general. Ellos
    incluyen el scalar la enumeración teclea, eg

    el color: := Rojo |
    la Naranja | Amarillo | Green | Azul | el Índigo | la
    Violeta

    y también nos da una manera de hacer la
    unión teclea, por ejemplo

    el bool_or_num: := el bool Izquierdo | el num
    Correcto

    Es interesante a nota que todo el datos básico
    teclea de Miranda pudo se defina de primeros principios, mientras
    usando ": := " las ecuaciones. Por ejemplo aquí son las
    definiciones del tipo para el bool, (natural) los números
    y listas,

    el bool: := Verdadero | Falso

    el nat: := Ceros | el nat de Suc

    la lista *: := el Nada | Hace trampas * (la lista
    *)

    Se hacen tipos teniendo como "num" construido en por las
    razones de eficacia – no es lógicamente
    necesario.

    También es posible asociar las "leyes" con los
    constructores de un tipo algebraico que es aplicado siempre que
    un objeto del tipo sea construido. Por ejemplo nosotros podemos
    asociar las leyes con el constructor del Nodo de el tipo del
    árbol anteriormente, para que los árboles siempre
    sean equilibrados. Nosotros omitimos la discusión de este
    rasgo de Miranda aquí para la falta de espacio – Los
    lectores interesados encontrarán más detalles en
    las referencias [Thompson 86, tornero 85].

    Teclee los Sinónimos

    El programador de Miranda puede introducir un nuevo
    nombre por un ya haber existido el tipo. Nosotros usamos "== "
    para estas definiciones, para distinguirlos de las definiciones
    de valor ordinarias. Los ejemplos

    el cordón == [el trabajo por horas]

    la matriz == [[el
    num]]

    Los sinónimos del tipo son completamente
    transparentes al typechecker – es mejor para pensar en ellos como
    los macros.
    También es posible introducir los sinónimos para
    las familias de tipos. Esto se hace usando los símbolos
    del tipo genéricos como los parámetros formales,
    como en

    la serie * == [[*]]

    así ahora el eg ` el num de la serie' es el mismo
    tipo como ` la matriz.'

    Los Tipos de los Datos abstractos

    Además de los tipos de hormigón, introdujo
    por ": := " o "== " las ecuaciones, Miranda permite la
    definición de tipos abstractos cuyo la aplicación
    está oculto del resto del programa. Para mostrar
    cómo esto trabaja nosotros

    dé el ejemplo normal de definir la pila como un
    tipo del datos abstracto (aquí basó en las
    listas):

    los abstype apilan *

    con vacío:: la pila *

    el isempty:: la pila * -> el bool

    el empujón:: * -> la pila * -> la pila
    *

    el estallido:: la pila * -> la pila *

    la cima:: la pila * -> *

    la pila * == [*]

    vacío = []

    el isempty x = (x = [])

    empuje un x = (el a:x)

    el estallido (el a:x) = x

    la cima (el a:x) = un

    Nosotros vemos que la definición de un tipo del
    datos abstracto consiste en dos las partes. Primero una
    declaración del formulario el "abstype… con… ",
    dónde lo siguiente de los nombres el "con" se llama la
    firma del lo abstracto los datos teclean. Estos nombres son la
    interfaz entre el tipo de los datos abstracto

    y el resto del programa. Entonces un juego de
    ecuaciones que dan las encuadernaciones para los nombres
    introducidos en la declaración del abstype. Éstos
    se llaman las ecuaciones de aplicación.

    La abstracción del tipo se da fuerza a por
    el typechecker. El mecanismo los trabajos como sigue. Cuando el
    typechecking las ecuaciones de aplicación el se tratan
    tipo abstracto y su representación como ser el mismo tipo.
    En el todo del resto de la escritura el tipo abstracto y su la
    representación se trata como dos separado y completamente
    el unrelated los tipos. Esto es algo diferente del mecanismo
    usual para llevando a cabo los datos abstractos teclea, pero
    tiene varios ventajas. Es discutido a la longitud algo mayor en
    [Tornero 85].

    La Recopilación separada y
    Uniéndose

    Los mecanismos básicos para la
    recopilación separada y unirse son sumamente simple.
    Cualquier escritura de Miranda puede contener uno o más
    directives del el formulario

    % incluya el "pathname"

    donde el "pathname" es el nombre de otro Miranda
    escritura archivo
    (qué poderío contiene incluya el directives, y
    así sucesivamente el recursively – ciclos en el incluya la
    estructura no se permite sin embargo). La visibilidad de nombres
    a una escritura incluyendo se controla por un director en el
    incluido la escritura, del formulario,

    % los nombres de la exportación

    Se permitía exportar los tipos así como
    los valores. No se permite para exportar un valor a un lugar
    dónde su tipo es desconocido, para que si usted exporta un
    objeto de un tipo localmente definido, los typename
    también deben exportarse. Exportando el nombre de un ": :=
    " teclee automáticamente exporta todos su constructores.
    Si una escritura no contiene una exportación director,
    entonces, el valor predeterminado es que todos los nombres (y
    typenames) define será exportado (pero no aquéllos
    por que adquirió% incluya las declaraciones).

    También se permitía escribir una escritura
    del paramaterized en que cierto los nombres y/o typenames se
    declaran como libre. Un ejemplo es que nosotros podría
    desear escribir un paquete por hacer el álgebra de
    la matriz sin saber lo que el tipo de los elementos de la matriz
    va a ser. Un título para tal un paquete podría
    parecerse:

    % libre {el elemento:: el tipo

    ponga a cero, unidad:: el elemento

    el mult, agregue, substraiga, divida::
    elemento->element->element

    }

    % el matmult de la exportación el eigenvectors
    del eigenvalues determinante…

    || aquí seguiría las definiciones de
    matmult, determinante,

    || el eigenvalues, etc. por lo que se refiere al cero de
    los identificadores libre,

    || la unidad, el mult, agrega, substraiga,
    divida

    En la escritura usando, el correspondiendo% incluya la
    declaración debe dar un ponga de encuadernaciones para las
    variables libres de la escritura incluido. Para el ejemplo
    aquí es un instantiation del paquete de la matriz esbozado
    sobre, con los números reales como el tipo del elemento
    escogido:

    % incluya el "matrix_pack"

    {el elemento == el num; ceros = 0; la unidad =
    1

    el mult = *; agregue = +; substraiga = -; divida =
    /

    }

    Los tres directivas incluyen la exportación, y
    libre proporcione el Miranda programador con un flexible y tipo
    el mecanismo seguro por estructurar los pedazos más
    grandes de software de las bibliotecas de
    componentes más pequeños.

    La recopilación separada se administra sin la
    intervención del usuario. Cada archivo que contiene una
    escritura de Miranda se sombrea por un archivo de código
    de objeto creado por el sistema, y se recrean los archivos de
    código de objeto automáticamente y relinked si
    ellos se vuelven fuera de fecha con respecto a cualquier
    pertinente la fuente. (Esta conducta es
    fuertemente análoga a eso logrado por el La UNIX programa
    hechura, sólo que aquí el usuario no se exige
    escribir un makefile – la información de la dependencia
    necesaria se infiere del % incluya el directives en la fuente de
    Miranda.)

    El Estado de Aplicación actual

    Una aplicación de Miranda está disponible
    para VAX, SOL-3, SOL-4, DECstation, MIPS, Apolo, y varias otras
    máquinas Berkeley corriente UNIX, y
    también para la serie de HP9000 bajo sistema 5. Esto es
    un

    aplicación interpretiva que trabaja compilando
    las escrituras de Miranda a un código del intermedio
    basó en el combinators. Está corriendo actualmente
    a 400 sitios (a partir del 1990 de enero). Autorizando la
    información pueden obtenerse de la dirección neta

    Aportes

    • Tratamiento sistemático de la sobrecarga a
      través de las clases de tipos.
    • Entrada/salida puramente funcional a través
      de Monadas para entrada/salida: permite realizar las tareas
      de Entrada/Salida de una forma puramente funcional,
      manteniendo transparencia referencial y sin tener efectos
      laterales. Desde el punto de vista del programador la monada
      es un tipo abstracto de dato, por lo tanto una monada de
      entrada salida provee operaciones primitivas de
      entrada/salida.
    • Definición y utilización de
      módulos.
    • Se pueden nombrar componentes de tipos de datos
      algebraicos.
    • Clases de constructores de tipos.
    • Currificación: Las funciones en
      Haskell tienen un único argumento y devuelven un
      único resultado. Si f toma un argumento de tipo
      T1 y devuelve un resultado de tipo T2 ,
      f es de tipo T1 ® T2 , expresado como f ::
      T1 ® T2. Para simular
      funciones con más de un argumento, simplemente se
      entiende que la función definida se aplica al primero
      de ellos y el resultado es otra función que se aplica
      al segundo, y así sucesivamente. Es decir, la
      expresión f x1 x2 …
      xn se interpreta como
      (…((fx1)x2)…) xn . Si
      T1 es el tipo de x1 , T2 el
      de x2 ,. . . ,Tn es el tipo de
      xn y T es el tipo del resultado, el tipo de f es:
      T1 ® (T2 ®
      (…(Tn ® T ))) abreviado como
      T1 ® T2 ® … ® Tn
      ®
      T

    Este proceso conocido como currificación
    en honor al matemático Haskell B. Curry, permite la
    creación de varias funciones mediante una sola
    definición. además permite reducir el

    número de paréntesis necesarios para
    escribir expresiones.

    Considérese la siguiente
    ecuación:

    sumar x y = x + y

    Suponiendo que el operador + suma enteros, la
    ecuación define una función
    sumar::Int®
    (Int®
    Int) que, abreviadamente, puede expresarse como
    sumar::Int ®
    Int ®
    Int. La expresión sumar 3 4 devuelve obviamente 7,
    pero en su evaluación intervienen dos funciones,
    primeramente la función sumar que, aplicada a 3, devuelve
    una función sin nombre que podemos llamar "sumar 3", y
    después esta segunda función que, aplicada a 4,
    devuelve 7. De hecho, sumar 3 es una expresión
    válida, cuyo tipo es Int -> Int.

    Partes: 1, 2

    Página siguiente 

    Nota al lector: es posible que esta página no contenga todos los componentes del trabajo original (pies de página, avanzadas formulas matemáticas, esquemas o tablas complejas, etc.). Recuerde que para ver el trabajo en su versión original completa, puede descargarlo desde el menú superior.

    Todos los documentos disponibles en este sitio expresan los puntos de vista de sus respectivos autores y no de Monografias.com. El objetivo de Monografias.com es poner el conocimiento a disposición de toda su comunidad. Queda bajo la responsabilidad de cada lector el eventual uso que se le de a esta información. Asimismo, es obligatoria la cita del autor del contenido y de Monografias.com como fuentes de información.

    Categorias
    Newsletter