Estaba pensando por qué hay bibliotecas estándar (en todos los lenguajes de programación que he aprendido, como C ++, Java, Python) como stdlib, en lugar de tener "funciones" similares como primitivas del lenguaje en sí.
programming-languages
standard-library
Simone Broili
fuente
fuente
Respuestas:
Permítanme ampliar un poco la buena respuesta de @ Vincent (+1) :
Puede, y lo hace a través de al menos dos mecanismos:
alinear una llamada de función : durante la traducción, el compilador puede reemplazar una llamada de código fuente con su implementación directamente en línea en lugar de hacer una llamada real a la función. Aún así, la función necesita tener una implementación definida en algún lugar y que pueda estar en la biblioteca estándar.
función intrínseca: las funciones intrínsecas son funciones de las que se ha informado al compilador sin encontrar necesariamente la función en una biblioteca. Por lo general, están reservados para funciones de hardware a las que prácticamente no se puede acceder de otra manera, ya que son tan simples que incluso la sobrecarga de una función de biblioteca de llamadas al lenguaje ensamblador se considera alta. (El compilador generalmente solo puede insertar automáticamente el código fuente en su idioma, pero no las funciones de ensamblaje, que es donde entra el mecanismo intrínseco).
Aún así, la mejor opción a veces es que el compilador traduzca una llamada de función en el idioma de origen a una llamada de función en el código de máquina. La recursividad, los métodos virtuales y el gran tamaño son algunas de las razones por las cuales la alineación no siempre es posible / práctica. (Otra razón es la intención de la compilación, como la compilación separada (módulos de objeto), unidades de carga separadas (por ejemplo, DLL)).
Tampoco hay una ventaja real en hacer que la mayoría de las funciones estándar de la biblioteca sean intrínsecas (eso codificaría mucho más conocimiento en el compilador sin ninguna ventaja real), por lo que una llamada de código de máquina nuevamente es a menudo lo más apropiado.
C es un lenguaje notable que posiblemente omitió otras declaraciones de lenguaje explícito a favor de las funciones estándar de la biblioteca. Aunque las bibliotecas preexistieron, este lenguaje hizo un cambio para hacer más trabajo de las funciones estándar de la biblioteca y menos como declaraciones explícitas en la gramática del lenguaje. IO en otros lenguajes, por ejemplo, con frecuencia recibió su propia sintaxis en forma de varias declaraciones, mientras que la gramática C no define ninguna declaración IO, simplemente difiere a su biblioteca estándar para proporcionar eso, todo accesible a través de llamadas a funciones, que el compilador ya sabe cómo hacerlo.
fuente
print
: en 2.x, era una declaración , con su propia gramática especial, pero en 3.x, se convirtió en otra llamada de función. Ver PEP 3105 para la explicación oficial.Esto es simplemente para mantener el lenguaje en sí lo más simple posible. Debe distinguir entre una característica del lenguaje, como un tipo de bucle o formas de pasar parámetros a funciones, etc., y la funcionalidad común que la mayoría de las aplicaciones necesitan.
Las bibliotecas son funciones que pueden ser útiles para muchos programadores, por lo que se crean como código reutilizable que se puede compartir. Las bibliotecas estándar están diseñadas para ser funciones muy comunes que los programadores suelen necesitar. De esta manera, el lenguaje de programación es inmediatamente útil para una gama más amplia de programadores. Las bibliotecas se pueden actualizar y ampliar sin cambiar las características principales del lenguaje en sí.
fuente
PHP
Como ejemplo, apenas hay diferencias entre sus vastas funciones del lenguaje y el lenguaje en sí.include
,require
yrequire_once
, si / for / while (programación estructurada), excepciones, un sistema separado de 'valores de error', complicada reglas de escritura débiles, complicada reglas de precedencia del operador, y así sucesivamente . Compare esto con la simplicidad de, por ejemplo, Smalltalk, Scheme, Prolog, Forth, etc.;)Además de lo que las otras respuestas ya han dicho, poner funciones estándar en una biblioteca es una separación de preocupaciones :
El trabajo del compilador es analizar el idioma y generar código para él. No es el trabajo del compilador contener nada que ya pueda escribirse en ese idioma y proporcionarse como una biblioteca.
Es el trabajo de la biblioteca estándar (la que siempre está implícitamente disponible) para proporcionar la funcionalidad central que necesitan prácticamente todos los programas. No es el trabajo de la biblioteca estándar contener todas las funciones que podrían ser útiles.
El trabajo de las bibliotecas estándar opcionales es proporcionar una funcionalidad auxiliar que muchos programas pueden prescindir, pero que siguen siendo bastante básicas y también esenciales para que muchas aplicaciones garanticen el envío con entornos estándar. No es el trabajo de esas bibliotecas opcionales contener todo el código reutilizable que se haya escrito.
El trabajo de las bibliotecas de usuario es proporcionar colecciones de funciones útiles reutilizables. No es el trabajo de las bibliotecas de usuario contener todo el código que se ha escrito.
El trabajo del código fuente de una aplicación es proporcionar los bits de código restantes que en realidad solo son relevantes para esa aplicación.
Si quieres un software único para todos, obtienes algo increíblemente complejo. Necesita modularizar para reducir la complejidad a niveles manejables. Y necesita modularizar para permitir implementaciones parciales :
La biblioteca de subprocesos no tiene valor en el controlador integrado de un solo núcleo. Permitir que la implementación del lenguaje para este controlador integrado simplemente no incluya la
pthread
biblioteca es lo correcto.La biblioteca matemática no tiene valor en el microcontrolador que ni siquiera tiene una FPU. Nuevamente, no verse obligado a proporcionar funciones como
sin()
hace la vida mucho más fácil para los implementadores de su lenguaje para ese microcontrolador.Incluso la biblioteca estándar central no tiene valor cuando está programando un núcleo. No puede implementar
write()
sin una syscall en el kernel, y no puede implementarprintf()
sinwrite()
. Como programador de kernel, es su trabajo proporcionar lawrite()
llamada al sistema, no puede esperar que esté allí.Un lenguaje que no permite tales omisiones de las bibliotecas estándar simplemente no es adecuado para muchas tareas . Si desea que su idioma sea flexiblemente utilizable en entornos poco comunes, debe ser flexible en las bibliotecas estándar que se incluyen. Cuanto más se base su lenguaje en las bibliotecas estándar, más suposiciones hará sobre su entorno de ejecución y, por lo tanto, restringirá su uso a entornos que brinden estos requisitos previos.
Por supuesto, los lenguajes de alto nivel como Python y Java pueden hacer muchas suposiciones sobre su entorno. Y tienden a incluir muchas cosas en sus bibliotecas estándar. Los lenguajes de nivel inferior como C proporcionan mucho menos en sus bibliotecas estándar y mantienen la biblioteca estándar central mucho más pequeña. Es por eso que encuentra un compilador de C que funciona para prácticamente cualquier arquitectura, pero es posible que no pueda ejecutar ningún script de Python en él.
fuente
Una razón importante por la que los compiladores y las bibliotecas estándar están separadas es porque tienen dos propósitos diferentes (incluso si ambos están definidos por la misma especificación de lenguaje): el compilador traduce el código de nivel superior en instrucciones de máquina, y la biblioteca estándar proporciona pruebas previamente implementaciones de funcionalidad comúnmente necesaria. Los escritores de compiladores valoran la modularidad al igual que otros desarrolladores de software. De hecho, algunos de los primeros compiladores de C dividieron aún más el compilador en programas separados para preprocesar, compilar y vincular.
Esta modularidad le ofrece muchas ventajas:
Históricamente hablando (al menos desde la perspectiva de C), las versiones originales de pre-estandarización del lenguaje no tenían una biblioteca estándar en absoluto. Los proveedores de sistemas operativos y terceros a menudo proporcionarían bibliotecas llenas de funcionalidades de uso común, pero diferentes implementaciones incluían cosas diferentes y eran en gran medida incompatibles entre sí. Cuando C se estandarizó, definieron una "biblioteca estándar" en un intento de armonizar estas implementaciones dispares y mejorar la portabilidad. La biblioteca estándar C se desarrolló por separado del lenguaje, como las bibliotecas Boost para C ++, pero luego se integraron en la especificación del lenguaje.
fuente
Respuesta adicional de caso de esquina: gestión de propiedad intelectual
Un ejemplo notable es la implementación de Math.Pow (doble, doble) en .NET Framework que Microsoft compró a Intel y sigue sin divulgarse incluso si el marco fue de código abierto. (Para ser precisos, en el caso anterior es una llamada interna en lugar de una biblioteca, pero la idea es válida). Una biblioteca separada del lenguaje en sí (teóricamente también un subconjunto de bibliotecas estándar) puede dar a los patrocinadores del lenguaje más flexibilidad para dibujar el línea entre lo que debe mantenerse transparente y lo que debe permanecer sin revelar (debido a sus contratos con terceros u otros motivos relacionados con la propiedad intelectual).
fuente
Math.Pow
no menciona ninguna compra, ni nada sobre Intel, y habla de personas que leen el código fuente de la implementación de la función.Errores y depuración.
Errores: todo el software tiene errores, su biblioteca estándar tiene errores y su compilador tiene errores. Como usuario del lenguaje, es mucho más fácil encontrar y solucionar estos errores cuando están en la biblioteca estándar en lugar de en el compilador.
Depuración: es mucho más fácil para mí ver un seguimiento de la pila de una biblioteca estándar y darme una idea de lo que podría estar yendo mal. Porque ese rastro de pila tiene código, entiendo. Por supuesto, puede profundizar más y también puede rastrear sus funciones intrínsecas, pero es mucho más fácil si está en un idioma que usa todo el tiempo día a día.
fuente
Esta es una excelente pregunta!
Lo último
El estándar C ++, por ejemplo, nunca especifica qué se debe implementar en el compilador o en la biblioteca estándar: solo se refiere a la implementación . Por ejemplo, los símbolos reservados están definidos tanto por el compilador (como intrínsecos) como por la biblioteca estándar, indistintamente.
Sin embargo, todas las implementaciones de C ++ que conozco tendrán el mínimo número posible de intrínsecos proporcionados por el compilador, y tanto como sea posible por la biblioteca estándar.
Por lo tanto, si bien es técnicamente factible definir la biblioteca estándar como una funcionalidad intrínseca en el compilador, parece que rara vez se usa en la práctica.
¿Por qué?
Consideremos la idea de mover alguna pieza de funcionalidad de la biblioteca estándar al compilador.
Ventajas:
Desventajas
std
) para experimentar.Esto significa que mover algo al compilador es costoso , ahora y en el futuro, y por lo tanto requiere un caso sólido. Para algunas piezas de funcionalidad, es necesario (no se pueden escribir como código regular), sin embargo, incluso así, vale la pena extraer piezas mínimas y genéricas para pasar al compilador y construir sobre ellas en la biblioteca estándar.
fuente
Como diseñador de idiomas, me gustaría hacerme eco de algunas de las otras respuestas aquí, pero proporcionarlo a través de los ojos de alguien que está construyendo un idioma.
Se debe especificar un lenguaje de programación utilizando algún lenguaje. Debe poder transmitir el significado detrás de cualquier programa escrito en su idioma. Este lenguaje es muy difícil de escribir, y aún más difícil de escribir bien. En general, tiende a ser una forma de inglés muy precisa y bien estructurada que se utiliza para transmitir significado no a la computadora, sino a otros desarrolladores, especialmente aquellos desarrolladores que escriben compiladores o intérpretes para su idioma. Aquí hay un ejemplo de la especificación C ++ 11, [intro.multithread / 14]:
Blek! Cualquiera que se haya lanzado a comprender cómo C ++ 11 maneja el subprocesamiento múltiple puede apreciar por qué la redacción debe ser tan opaca, pero eso no perdona el hecho de que es ... bueno ... ¡tan opaco!
Compare eso con la definición de
std::shared_ptr<T>::reset
, en la sección de la biblioteca del estándar:Entonces, ¿cuál es la diferencia? En la parte de definición del lenguaje, los escritores no pueden asumir que el lector comprende las primitivas del lenguaje. Todo debe especificarse cuidadosamente en prosa inglesa. Una vez que llegamos a la parte de definición de la biblioteca, podemos usar el lenguaje para especificar el comportamiento. ¡Esto es a menudo mucho más fácil!
En principio, uno podría tener una construcción suave a partir de primitivas al comienzo del documento de especificaciones, hasta definir lo que podríamos considerar como "características de biblioteca estándar", sin tener que trazar una línea entre "primitivas de lenguaje" y características de "biblioteca estándar". En la práctica, esa línea resulta enormemente valiosa de dibujar porque le permite escribir algunas de las partes más complejas del lenguaje (como las que deben implementar algoritmos) utilizando un lenguaje diseñado para expresarlas.
Y de hecho, vemos algunas líneas borrosas:
java.lang.ref.Reference<T>
pueden ser subclasificadas por las clases de biblioteca estándar y porque los comportamientos de están tan profundamente entrelazados con la especificación del lenguaje Java que debieron poner algunas restricciones en la parte de ese proceso implementado como clases de "biblioteca estándar".java.lang.ref.WeakReference<T>
java.lang.ref.SoftReference<T>
java.lang.ref.PhantomReference<T>
Reference
fuente
Esto se entiende como una adición a las respuestas existentes (y es demasiado largo para un comentario).
Existen al menos otras dos razones para una biblioteca estándar:
Barrera de entrada
Si una característica de idioma en particular está en una función de biblioteca y quiero saber cómo funciona, puedo leer la fuente de esa función. Si deseo enviar un informe de error / solicitud de parche / extracción, generalmente no es demasiado difícil codificar una solución y probar los casos. Si está en el compilador, tengo que poder profundizar en lo interno. Incluso si está en el mismo idioma (y debería estarlo, cualquier compilador que se respete a sí mismo debería ser autohospedado) el código del compilador no se parece en nada al código de la aplicación. Puede llevar una eternidad incluso encontrar los archivos correctos.
Te estás desconectando de muchos contribuyentes potenciales si sigues esa ruta.
Carga de código caliente
Muchos idiomas ofrecen esta característica en un grado u otro, pero sería enormemente complicado volver a cargar en caliente el código que está haciendo la recarga en caliente. Si el SL está separado del tiempo de ejecución, se puede volver a cargar.
fuente
Esta es una pregunta interesante, pero ya hay muchas buenas respuestas, así que no intentaré una completa.
Sin embargo, dos cosas que no creo que hayan recibido suficiente atención:
Primero es que no todo está muy claro. Es un poco de espectro exactamente porque hay razones para hacer las cosas de manera diferente. Como ejemplo, los compiladores a menudo conocen las bibliotecas estándar y sus funciones. Ejemplo del ejemplo: la función "Hello World" de C, printf, es la mejor que se me ocurre. Es una función de biblioteca, tiene que ser así, ya que depende mucho de la plataforma. Pero el compilador debe conocer su comportamiento (implementación definida) para advertir al programador sobre invocaciones incorrectas. Esto no es particularmente bueno, pero fue visto como un buen compromiso. Por cierto, esta es la respuesta real a la mayoría de las preguntas de "por qué este diseño": mucho compromiso y "parecía una buena idea en ese momento". No siempre el "esta era la forma clara de hacerlo" o "
En segundo lugar, permite que la biblioteca estándar no sea tan estándar. Hay muchas situaciones en las que un lenguaje es deseable, pero las bibliotecas estándar que generalmente lo acompañan no son prácticas y deseables. Este es el caso más común con lenguajes de programación de sistemas como C, en plataformas no estándar. Por ejemplo, si tiene un sistema sin un sistema operativo o un planificador: no tendrá subprocesos.
Con un modelo de biblioteca estándar (y los subprocesos son compatibles), esto se puede manejar de manera limpia: el compilador es prácticamente el mismo, puede reutilizar los bits de las bibliotecas que se aplican, y todo lo que no se puede eliminar. Si esto se incluye en el compilador, las cosas comienzan a complicarse.
Por ejemplo:
No puedes ser un compilador compatible.
¿Cómo indicaría su desviación del estándar? Tenga en cuenta que generalmente hay alguna forma de sintaxis de importación / inclusión que puede tener un error, es decir, la importación de pitones o la inclusión de C que apunta fácilmente al problema si falta algo en el modelo de biblioteca estándar.
También se aplican problemas similares si desea modificar o ampliar la funcionalidad de 'biblioteca'. Esto es mucho más común de lo que piensas. Solo para seguir con el enhebrado: Windows, Linux y algunas unidades de procesamiento de red exóticas hacen el enhebrado de manera muy diferente. Si bien los bits de Linux / Windows pueden ser bastante estáticos y poder usar una API idéntica, las cosas de la NPU cambiarán con el día de la semana y la API con ella. Los compiladores se desviarían rápidamente a medida que la gente decidiera qué partes necesitaban soportar / podrían hacer con bastante rapidez si no había forma de dividir este tipo de cosas.
fuente